diff --git a/.github/workflows/build_microservices.yml b/.github/workflows/build_microservices.yml index 4295518e5..4796bdeb9 100644 --- a/.github/workflows/build_microservices.yml +++ b/.github/workflows/build_microservices.yml @@ -36,10 +36,10 @@ jobs: with: fetch-depth: 0 - - name: Setup .NET 8.0.x + - name: Setup .NET 8.0.401 uses: actions/setup-dotnet@v2 with: - dotnet-version: '8.0.x' + dotnet-version: '8.0.401' - name: Restore dependencies for ${{ matrix.project }} run: dotnet restore ${{ matrix.project }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 04648ae38..22b723941 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,13 +13,23 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get update && sudo apt-get install -y libicu-dev + - name: Set globalization invariant mode + run: | + export DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -37,9 +47,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update @@ -61,9 +76,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update @@ -85,13 +105,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -109,13 +136,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -133,13 +167,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -157,13 +198,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -181,13 +229,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -205,13 +260,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -229,13 +291,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -253,13 +322,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -277,13 +353,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -301,13 +384,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: @@ -325,13 +415,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - uses: actions/setup-dotnet@v3 with: - dotnet-version: '3.1.x' + dotnet-version: '8.x' - name: Install .NET 8.x run: | sudo apt-get update sudo apt-get install -y dotnet-sdk-8.0 + - name: Install ICU package + run: sudo apt-get install -y libicu-dev - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: diff --git a/.github/workflows/issue_opened_responder.yml b/.github/workflows/issue_opened_responder.yml new file mode 100644 index 000000000..4ea26461b --- /dev/null +++ b/.github/workflows/issue_opened_responder.yml @@ -0,0 +1,51 @@ +name: Automatic Issue Responder + +on: + issues: + types: [opened] + +jobs: + issue-response: + runs-on: ubuntu-latest + steps: + - name: Post a thank you comment on the new issue + uses: actions/github-script@v5 + with: + script: | + const issueNumber = context.issue.number; + const issueTitle = context.payload.issue.title; + const owner = context.payload.repository.owner.login; + const repo = context.payload.repository.name; + + github.issues.createComment({ + owner: owner, + repo: repo, + issue_number: issueNumber, + body: ` + 🌟 **Thank you for your contribution to Astravent!** 🌟 + + We truly appreciate your effort in helping improve our distributed project. Every issue helps make Astravent better, and your feedback brings us closer to providing a more robust and seamless experience for all. + + Our team, including the code owners, has been notified and will carefully review your issue. In the meantime, if you have any additional context or updates to share, feel free to add them to this thread. + + We're excited to see where your contribution leads! 🚀 + + Stay awesome, + The Astravent Team + ` + }); + + - name: Notify the code owner via issue comment + uses: actions/github-script@v5 + with: + script: | + const issueNumber = context.issue.number; + const codeOwner = "CODEOWNER_USERNAME"; // Replace with the GitHub username of the code owner + + // Notify the code owner by mentioning them in the issue comment + github.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: `@${codeOwner}, a new issue (#${issueNumber}) has been created and may require your attention.` + }); diff --git a/.github/workflows/ossaranalyzer.yml b/.github/workflows/ossaranalyzer.yml index 068f4c0b6..299af6291 100644 --- a/.github/workflows/ossaranalyzer.yml +++ b/.github/workflows/ossaranalyzer.yml @@ -23,6 +23,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Remove global.json (if exists) + run: | + if [ -f "global.json" ]; then + rm global.json + fi - name: Run OSSAR uses: github/ossar-action@v1 id: ossar diff --git a/MiniSpace.APIGateway.Nuar/.dockerignore b/MiniSpace.APIGateway.Nuar/.dockerignore new file mode 100644 index 000000000..1f9f9ea64 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/.dockerignore @@ -0,0 +1,29 @@ +**/[Oo]bj +**/[Bb]in +**/[Ll]ogs +TestResults/ +.nuget/ +_ReSharper.*/ +packages/ +artifacts/ +PublishProfiles/ +*.user +*.suo +*.cache +*.docstates +_ReSharper.* +nuget.exe +*net45.csproj +*k10.csproj +*.psess +*.vsp +*.pidb +*.userprefs +*DS_Store +*.ncrunchsolution +*.*sdf +*.ipch +.vs/ +project.lock.json +bower_components/ +node_modules/ \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/.gitignore b/MiniSpace.APIGateway.Nuar/.gitignore new file mode 100644 index 000000000..b95ef7864 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/.gitignore @@ -0,0 +1,335 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +# **/Properties/launchSettings.json + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +logs/ + +# Ignore appsettings.json files +**/appsettings*.json \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/Dockerfile b/MiniSpace.APIGateway.Nuar/Dockerfile new file mode 100644 index 000000000..11c8514cc --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/Dockerfile @@ -0,0 +1,17 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /app + +COPY . . + +RUN dotnet publish src/MiniSpace.APIGateway -c Release -o out + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 +WORKDIR /app + +COPY --from=build /app/out . + +ENV ASPNETCORE_URLS=http://*:80 +ENV ASPNETCORE_ENVIRONMENT=docker +ENV NTRADA_CONFIG=ntrada.docker + +ENTRYPOINT ["dotnet", "MiniSpace.APIGateway.dll"] diff --git a/MiniSpace.APIGateway.Nuar/LICENSE b/MiniSpace.APIGateway.Nuar/LICENSE new file mode 100644 index 000000000..c3a0bdfb9 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/LICENSE @@ -0,0 +1,89 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +## TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +### 1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, that is made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +### 2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +### 3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +### 4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + +(b) You must cause any modified files to carry prominent notices stating that You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright notices to Your version of this material, provided that such additional copyright notices are not considered to be modifications of the License. + +### 5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +### 6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +### 7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +### 8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +### 9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, protection, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +## END OF TERMS AND CONDITIONS + +### Appendix: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be placed in the LICENSE file in the root directory of your source code (if not already present): + + Copyright 2024 distributed_minispace_team + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/MiniSpace.APIGateway.Nuar/MiniSpace-sample-scenario.rest b/MiniSpace.APIGateway.Nuar/MiniSpace-sample-scenario.rest new file mode 100644 index 000000000..35a963a71 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/MiniSpace-sample-scenario.rest @@ -0,0 +1,198 @@ +@api = http://localhost:5000 + +### At first, create an account +POST {{api}}/identity/sign-up +Content-Type: application/json + +{ + "email": "minispace-user1@mailinator.com", + "password": "secret", + "role": "user" +} + +### Authenticate and grab the access token +# @name sign_in +POST {{api}}/identity/sign-in +Content-Type: application/json + +{ + "email": "minispace-user1@mailinator.com", + "password": "secret" +} + + +### Get your user account details +@accessToken = {{sign_in.response.body.$.accessToken}} +GET {{api}}/identity/me +Authorization: Bearer {{accessToken}} + +### Complete the customer registration process +POST {{api}}/customers +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "fullName": "John Doe", + "address": "New York" +} + +### Get your customer account details +GET {{api}}/customers/me +Authorization: Bearer {{accessToken}} + +### Add a parcel and grab its id +# @name add_parcel +POST {{api}}/parcels +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "variant": "weapon", + "size": "large", + "name": "Parcel #1", + "description": "My parcel #1" +} + + +### Get your parcels +@parcelId = {{add_parcel.response.headers.Resource-ID}} +GET {{api}}/parcels +Authorization: Bearer {{accessToken}} + +### Calculate the parcel volume to see whether it works as expected +GET {{api}}/parcels/volume?parcelIds=["{{parcelId}}"] +Authorization: Bearer {{accessToken}} + +### Create a new order and grab its id +# @name create_order +POST {{api}}/orders +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ +} + + +### Get your orders +@orderId = {{create_order.response.headers.Resource-ID}} +GET {{api}}/orders +Authorization: Bearer {{accessToken}} + +### Add a parcel to the order +POST {{api}}/orders/{{orderId}}/parcels/{{parcelId}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ +} + +### Get your order details which should now contain a package +GET {{api}}/orders/{{orderId}} +Authorization: Bearer {{accessToken}} + +### Add a new vehicle +# @name add_vehicle +POST {{api}}/vehicles +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "brand": "Brand", + "model": "Model", + "description": "Vehicle description", + "payloadCapacity": 1000, + "loadingCapacity": 1000, + "pricePerService": 100, + "variants": 1 +} + + +### Get a newly added vehicle +@vehicleId = {{add_vehicle.response.headers.Resource-ID}} +GET {{api}}/vehicles?payloadCapacity=0&loadingCapacity=0&variants=1 +Authorization: Bearer {{accessToken}} + +### Add a vehicle as the available resource being able to deliver the pacakge +@resourceId = {{add_vehicle.response.headers.Resource-ID}} +@tags = ["vehicle", "armor"] + +POST {{api}}/availability/resources +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "resourceId": "{{resourceId}}", + "tags": {{tags}} +} + +### Browse the resources filtered by tags if needed +GET {{api}}/availability/resources?tags={{tags}}&matchAllTags=false +Authorization: Bearer {{accessToken}} + +### Assign a vehicle to your order and set the desired delivery date +@deliveryDate = 2020-01-10 + +POST {{api}}/orders/{{orderId}}/vehicles/{{vehicleId}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "deliveryDate": "{{deliveryDate}}" +} + +### Make a reservation for the given date to deliver the package +POST {{api}}/availability/resources/{{resourceId}}/reservations/{{deliveryDate}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "priority": 0 +} + +### Ensure that resource was reserved for the chosen date +GET {{api}}/availability/resources/{{resourceId}} +Authorization: Bearer {{accessToken}} + + +### Start the delivery and grab its id +# @name start_delivery +POST {{api}}/deliveries +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "orderId": "{{orderId}}", + "description": "Delivery description", + "dateTime": "{{deliveryDate}}" +} + + + +### Add some internal delivery transportation details +@deliveryId = {{start_delivery.response.headers.Resource-ID}} +POST {{api}}/deliveries/{{deliveryId}}/registrations +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "id": "{{deliveryId}}", + "description": "Delivery registration description", + "dateTime": "{{deliveryDate}}" +} + +### Complete the delivery +POST {{api}}/deliveries/{{deliveryId}}/complete +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "id": "{{deliveryId}}" +} + +### Check the delivery details +GET {{api}}/deliveries/{{deliveryId}} +Authorization: Bearer {{accessToken}} + +### Get your order details which should now be marked as completed +GET {{api}}/orders/{{orderId}} +Authorization: Bearer {{accessToken}} \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/MiniSpace.APIGateway.sln b/MiniSpace.APIGateway.Nuar/MiniSpace.APIGateway.sln new file mode 100644 index 000000000..5d6d7c0f3 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/MiniSpace.APIGateway.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F3265023-C8FE-447E-A494-11CF88B88C0E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "minispace.APIGateway", "src\minispace.APIGateway\minispace.APIGateway.csproj", "{231172E7-4D7D-4E00-8EF4-23C564F57F03}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Debug|x64.ActiveCfg = Debug|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Debug|x64.Build.0 = Debug|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Debug|x86.ActiveCfg = Debug|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Debug|x86.Build.0 = Debug|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Release|Any CPU.Build.0 = Release|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Release|x64.ActiveCfg = Release|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Release|x64.Build.0 = Release|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Release|x86.ActiveCfg = Release|Any CPU + {231172E7-4D7D-4E00-8EF4-23C564F57F03}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {231172E7-4D7D-4E00-8EF4-23C564F57F03} = {F3265023-C8FE-447E-A494-11CF88B88C0E} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.APIGateway.Nuar/MiniSpace.rest b/MiniSpace.APIGateway.Nuar/MiniSpace.rest new file mode 100644 index 000000000..5e1c8952f --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/MiniSpace.rest @@ -0,0 +1,289 @@ +@api = http://localhost:5000 +@accessToken = xxx.xxx.xxx +@customerId = 00000000-0000-0000-0000-000000000000 +@operationId = 00000000-0000-0000-0000-000000000000 +@parcelId = 00000000-0000-0000-0000-000000000000 +@orderId = 00000000-0000-0000-0000-000000000000 +@vehicleId = 00000000-0000-0000-0000-000000000000 +@resourceId = 00000000-0000-0000-0000-000000000000 +@deliveryId = 00000000-0000-0000-0000-000000000000 +@dateTime = 2020-01-10 +@deliveryDate = 2020-01-10 +@orderPrice = 100 +@state = valid +@tags = ["vehicle", "armor"] + + +# ============== IDENTITY ==================== # + +### POST sign-up +POST {{api}}/identity/sign-up +Content-Type: application/json + +{ + "email": "admin-user1@mailinator.com", + "password": "secret", + "role": "admin" +} + +### POST sign-in +POST {{api}}/identity/sign-in +Content-Type: application/json + +{ + "email": "admin-user1@mailinator.com", + "password": "secret" +} + +### GET me +GET {{api}}/identity/me +Authorization: Bearer {{accessToken}} + + +# ============== CUSTOMERS ==================== # + +### +GET {{api}}/customers +Authorization: Bearer {{accessToken}} + +### +GET {{api}}/customers/me +Authorization: Bearer {{accessToken}} + +### +GET {{api}}/customers/{{customerId}} +Authorization: Bearer {{accessToken}} + +### +GET {{api}}/customers/{{customerId}}/state +Authorization: Bearer {{accessToken}} + +### +POST {{api}}/customers +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "fullName": "John Doe", + "address": "New York" +} + +### +PUT {{api}}/customers/{{customerId}}/state/{{state}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "customerId": "{{customerId}}", + "state": "{{state}}" +} + +# ============== AVAILABILITY ==================== # + +### +GET {{api}}/availability/resources?tags={{tags}}&matchAllTags=false + +### +GET {{api}}/availability/resources/{{resourceId}} +Authorization: Bearer {{accessToken}} + +### +POST {{api}}/availability/resources +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "resourceId": "{{resourceId}}", + "tags": {{tags}} +} + +### +POST {{api}}/availability/resources/{{resourceId}}/reservations/{{dateTime}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "resourceId": "{{resourceId}}", + "customerId": "{{customerId}}", + "dateTime": "{{dateTime}}", + "priority": 0 +} + +### +DELETE {{api}}/availability/resources/{{resourceId}}/reservations/{{dateTime}} +Authorization: Bearer {{accessToken}} + +### +DELETE {{api}}/availability/resources/{{resourceId}} +Authorization: Bearer {{accessToken}} + +# ============== DELIVERIES ==================== # + +### +GET {{api}}/deliveries/{{orderId}} +Authorization: Bearer {{accessToken}} + +### +POST {{api}}/deliveries +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "orderId": "{{orderId}}", + "description": "Delivery description", + "dateTime": "2019-10-10" +} + +### +POST {{api}}/deliveries/{{deliveryId}}/fail +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "id": "{{deliveryId}}", + "reason": "Delivery failed" +} + +### +POST {{api}}/deliveries/{{deliveryId}}/complete +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "id": "{{deliveryId}}" +} + +### +POST {{api}}/deliveries/{{deliveryId}}/registrations +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "id": "{{deliveryId}}", + "description": "Delivery registration description", + "dateTime": "2019-10-10" +} + +# ============== OPERATIONS ==================== # + +### +GET {{api}}/operations/{{operationId}} + +# ============== ORDERS ==================== # + +### +GET {{api}}/orders +Authorization: Bearer {{accessToken}} + +### +GET {{api}}/orders/{{orderId}} +Authorization: Bearer {{accessToken}} + +### +POST {{api}}/orders +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ +} + +### +DELETE {{api}}/orders/{{orderId}} +Authorization: Bearer {{accessToken}} + +### +POST {{api}}/orders/{{orderId}}/parcels/{{parcelId}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ +} + +### +POST {{api}}/orders/{{orderId}}/vehicles/{{vehicleId}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "deliveryDate": "{{deliveryDate}}" +} + +### +DELETE {{api}}/orders/{{orderId}}/parcels/{{parcelId}} +AuthorizatiOrderDiscountPriceon: Bearer {{accessToken}} + +# ============== PARCELS ==================== # + +### +GET {{api}}/parcels +Authorization: Bearer {{accessToken}} + +### +GET {{api}}/parcels/{{parcelId}} +Authorization: Bearer {{accessToken}} + +### +GET {{api}}/parcels/volume?parcelIds=["{{parcelId}}"] +Authorization: Bearer {{accessToken}} + +### +POST {{api}}/parcels +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "variant": "weapon", + "size": "large", + "name": "Parcel #1", + "description": "My parcel #1" +} + +### +DELETE {{api}}/parcels/{{parcelId}} +Authorization: Bearer {{accessToken}} + +# ============== PRICING ==================== # + +### +GET {{api}}/pricing?orderPrice={{orderPrice}} +Authorization: Bearer {{accessToken}} + +# ============== VEHICLES ==================== # + +### +GET {{api}}/vehicles?payloadCapacity=0&loadingCapacity=0&variants=1 +Authorization: Bearer {{accessToken}} + +### +GET {{api}}/vehicles/{{vehicleId}} +Authorization: Bearer {{accessToken}} + +### +POST {{api}}/vehicles +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "brand": "Brand", + "model": "Model", + "description": "Vehicle description", + "payloadCapacity": 1000, + "loadingCapacity": 1000, + "pricePerService": 100, + "variants": 1 +} + +### +PUT {{api}}/vehicles/{{vehicleId}} +Authorization: Bearer {{accessToken}} +Content-Type: application/json + +{ + "id": "{{vehicleId}}", + "description": "Updated vehicle description", + "pricePerService": 150, + "variants": 2 +} + +### +DELETE {{api}}/vehicles/{{vehicleId}} \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/README.md b/MiniSpace.APIGateway.Nuar/README.md new file mode 100644 index 000000000..cd56c1521 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/README.md @@ -0,0 +1 @@ +# MiniSpace ApiGateway for the microinfrastructure \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/scripts/build.sh b/MiniSpace.APIGateway.Nuar/scripts/build.sh new file mode 100644 index 000000000..3affad0eb --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/scripts/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet build -c release \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/scripts/dockerize-tag-push.sh b/MiniSpace.APIGateway.Nuar/scripts/dockerize-tag-push.sh new file mode 100755 index 000000000..c4a70689f --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/scripts/dockerize-tag-push.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +export ASPNETCORE_ENVIRONMENT=docker + +cd .. + +docker build -t minispace.apigateway:latest . + +docker tag minispace.apigateway:latest adrianvsaint/minispace.apigateway:latest + +docker push adrianvsaint/minispace.apigateway:latest diff --git a/MiniSpace.APIGateway.Nuar/scripts/dockerize.sh b/MiniSpace.APIGateway.Nuar/scripts/dockerize.sh new file mode 100644 index 000000000..78ff42ece --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/scripts/dockerize.sh @@ -0,0 +1,21 @@ +#!/bin/bash +TAG='' +VERSION_TAG= + +case "$TRAVIS_BRANCH" in + "master") + TAG=latest + VERSION_TAG=$TRAVIS_BUILD_NUMBER + ;; + "develop") + TAG=dev + VERSION_TAG=$TAG-$TRAVIS_BUILD_NUMBER + ;; +esac + +REPOSITORY=$DOCKER_USERNAME/minispace.apigateway + +docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD +docker build -t $REPOSITORY:$TAG -t $REPOSITORY:$VERSION_TAG . +docker push $REPOSITORY:$TAG +docker push $REPOSITORY:$VERSION_TAG \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/scripts/start-async.sh b/MiniSpace.APIGateway.Nuar/scripts/start-async.sh new file mode 100755 index 000000000..673c184f8 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/scripts/start-async.sh @@ -0,0 +1,5 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=local +export NTRADA_CONFIG=ntrada-async +cd src/MiniSpace.APIGateway +dotnet run diff --git a/MiniSpace.APIGateway.Nuar/scripts/start.sh b/MiniSpace.APIGateway.Nuar/scripts/start.sh new file mode 100755 index 000000000..2d79cb22e --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/scripts/start.sh @@ -0,0 +1,4 @@ +#!/bin/bash +export ASPNETCORE_ENVIRONMENT=local +cd ../src/MiniSpace.APIGateway +dotnet run diff --git a/MiniSpace.APIGateway.Nuar/scripts/test.sh b/MiniSpace.APIGateway.Nuar/scripts/test.sh new file mode 100644 index 000000000..6046c35a0 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/scripts/test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet test \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/.gitignore b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/.gitignore new file mode 100644 index 000000000..f3a87d104 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/.gitignore @@ -0,0 +1,3 @@ +appsettings.local.json +appsettings.docker.json +appsettings.json diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/CorrelationContext.cs b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/CorrelationContext.cs new file mode 100644 index 000000000..f5a9a7c8f --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/CorrelationContext.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace MiniSpace.APIGateway.Infrastructure +{ + internal class CorrelationContext + { + public string CorrelationId { get; set; } + public string SpanContext { get; set; } + public UserContext User { get; set; } + public string ResourceId { get; set; } + public string TraceId { get; set; } + public string ConnectionId { get; set; } + public string Name { get; set; } + public DateTime CreatedAt { get; set; } + + public class UserContext + { + public string Id { get; set; } + public bool IsAuthenticated { get; set; } + public string Role { get; set; } + public IDictionary Claims { get; set; } + } + } +} \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/CorrelationContextBuilder.cs b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/CorrelationContextBuilder.cs new file mode 100644 index 000000000..ebc1e4e89 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/CorrelationContextBuilder.cs @@ -0,0 +1,57 @@ +using System; +using System.Linq; +using System.Security.Claims; +using Microsoft.Extensions.DependencyInjection; +using Nuar; +using Nuar.RabbitMq; +using OpenTracing; + +namespace MiniSpace.APIGateway.Infrastructure +{ + internal sealed class CorrelationContextBuilder : IContextBuilder + { + private readonly IServiceProvider _serviceProvider; + + public CorrelationContextBuilder(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public object Build(ExecutionData executionData) + { + var tracer = _serviceProvider.GetService(); + var spanContext = tracer is null ? string.Empty : + tracer.ActiveSpan is null ? string.Empty : tracer.ActiveSpan.Context.ToString(); + + var name = string.Empty; + if (executionData.Route.Config is {} && + executionData.Route.Config.TryGetValue("routing_key", out var routingKey)) + { + name = routingKey ?? string.Empty; + } + + if (string.IsNullOrWhiteSpace(name)) + { + name = $"{executionData.Context.Request.Method} {executionData.Context.Request.Path}"; + } + + return new CorrelationContext + { + CorrelationId = executionData.RequestId, + User = new CorrelationContext.UserContext + { + Id = executionData.UserId, + Claims = executionData.Claims, + Role = executionData.Claims.FirstOrDefault(c => c.Key == ClaimTypes.Role).Value, + IsAuthenticated = !string.IsNullOrWhiteSpace(executionData.UserId) + }, + ResourceId = executionData.ResourceId, + TraceId = executionData.TraceId, + ConnectionId = executionData.Context.Connection.Id, + Name = name, + CreatedAt = DateTime.UtcNow, + SpanContext = spanContext + }; + } + } +} \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/HttpRequestHook.cs b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/HttpRequestHook.cs new file mode 100644 index 000000000..2bf7a9024 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/HttpRequestHook.cs @@ -0,0 +1,28 @@ +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Nuar; +using Nuar.RabbitMq; +using Nuar.Hooks; + +namespace MiniSpace.APIGateway.Infrastructure +{ + internal sealed class HttpRequestHook : IHttpRequestHook + { + private readonly IContextBuilder _contextBuilder; + + public HttpRequestHook(IContextBuilder contextBuilder) + { + _contextBuilder = contextBuilder; + } + + + public Task InvokeAsync(HttpRequestMessage request, ExecutionData data) + { + var context = JsonConvert.SerializeObject(_contextBuilder.Build(data)); + request.Headers.TryAddWithoutValidation("Correlation-Context", context); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/SpanContextBuilder.cs b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/SpanContextBuilder.cs new file mode 100644 index 000000000..82a306eca --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Infrastructure/SpanContextBuilder.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Nuar; +using Nuar.RabbitMq; +using OpenTracing; + +namespace MiniSpace.APIGateway.Infrastructure +{ + internal sealed class SpanContextBuilder : ISpanContextBuilder + { + private readonly IServiceProvider _serviceProvider; + + public SpanContextBuilder(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public string Build(ExecutionData executionData) + { + var tracer = _serviceProvider.GetService(); + var spanContext = tracer is null ? string.Empty : + tracer.ActiveSpan is null ? string.Empty : tracer.ActiveSpan.Context.ToString(); + + return spanContext; + } + } +} \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/MiniSpace.APIGateway.csproj b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/MiniSpace.APIGateway.csproj new file mode 100644 index 000000000..eacf5a61b --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/MiniSpace.APIGateway.csproj @@ -0,0 +1,58 @@ + + + + net8.0 + latest + MiniSpace.APIGateway + + + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + Always + + + + + + + + \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/MiniSpace.APIGateway.sln b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/MiniSpace.APIGateway.sln new file mode 100644 index 000000000..911eb6cdd --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/MiniSpace.APIGateway.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.APIGateway", "MiniSpace.APIGateway.csproj", "{E93B31EF-EF44-4582-A700-C04DBAEE9800}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E93B31EF-EF44-4582-A700-C04DBAEE9800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E93B31EF-EF44-4582-A700-C04DBAEE9800}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E93B31EF-EF44-4582-A700-C04DBAEE9800}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E93B31EF-EF44-4582-A700-C04DBAEE9800}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6213D6E8-5ECD-459E-B9CC-C780B2FC92B0} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Program.cs b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Program.cs new file mode 100644 index 000000000..9adff9002 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Program.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Paralax; +using Paralax.Logging; +using Paralax.Metrics.AppMetrics; +using Paralax.Security; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Nuar; +using Nuar.RabbitMq; +using Nuar.Hooks; +using MiniSpace.APIGateway.Infrastructure; + +namespace MiniSpace.APIGateway +{ + public static class Program + { + public static Task Main(string[] args) + => CreateHostBuilder(args).Build().RunAsync(); + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseSetting(WebHostDefaults.DetailedErrorsKey, "true"); + webBuilder.CaptureStartupErrors(true); + webBuilder.ConfigureAppConfiguration(builder => + { + const string extension = "yml"; + var ntradaConfig = Environment.GetEnvironmentVariable("NTRADA_CONFIG"); + var configPath = args?.FirstOrDefault() ?? ntradaConfig ?? $"nuar.{extension}"; + if (!configPath.EndsWith($".{extension}")) + { + configPath += $".{extension}"; + } + + builder.AddYamlFile(configPath, false); + }) + .ConfigureServices(services => services.AddNuar() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddParalax() + .AddMetrics() + .AddSecurity()) + .Configure(app => app.UseNuar()) + .UseLogging(); + }); + + } +} diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Properties/launchSettings.json b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Properties/launchSettings.json new file mode 100644 index 000000000..c0c6c75d3 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/Properties/launchSettings.json @@ -0,0 +1,26 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:5000" + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": false, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "local" + } + }, + "MiniSpace.APIGateway": { + "commandName": "Project", + "launchBrowser": false, + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "local" + } + } + } +} \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/certs/localhost.cer b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/certs/localhost.cer new file mode 100644 index 000000000..31fc2df7e --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/certs/localhost.cer @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDCTCCAfGgAwIBAgIUFLglzcz4ySPop6UDOY4VfocxoSowDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIwMDMwMTExMjU1MloXDTIxMDMw +MTExMjU1MlowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAs7oyRinTJp0bER/BMpO/zNiNlW/KXQsP+RKxHCTd4Sjq +X9bdp1IkqMQtDJnxCMHjfmKkPEXLa7BlrmkMXZ3r54ociq0tbUa/S7S6s3oN17Ov +IwM3ohT+eXccTPx1UbrX8hrFfKabIp1e8ARJNsr+UJ1TeQc4h8rZLoCa0JbMXMyQ +bqZ1X8C6hOpKzgB+SwmsIm2w5eRqnbvxwXNS5hyxfL8r6XRObbJNb36xGE0WBjy9 +DIIiChlq8VOldqNBqGjRpN7bIxnVpSLeKq4Y2+jMPeoC9gr2Ncsjbrddac6ePUXj +UebC2Yb/5rL2PyebEWBmVa+OMN1QopQYhyI5hE+oswIDAQABo1MwUTAdBgNVHQ4E +FgQUpKJM7NhUzpmzGare25X3rNoml40wHwYDVR0jBBgwFoAUpKJM7NhUzpmzGare +25X3rNoml40wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfAFt +SUYlg1zSFmpt5338EJRaTCu0wmXeg+Hn73Bdi3Pa6qh3v+sgaRGzdJXdBLHZhhWB +h+gMRrJQ+ALNzh2vcPH6gS/du4tfFHfRufI2GalN+o95UUiiDmen9yGkyYsuEQp/ +mKYpLHvzsj6Qsx6++N7eI5nQ2d88OE753YPxZbWTUOr1voGodKCgJB36e5Rh2Lvs +Uqn8ePtKPewqjfcKE3wCcN5HNpiC+GyK9SkzPGDuh4VGzZCMA83vuWrYNOE5rMOs +G8NGFdqZ4Pk8wT4RQhJkPEAT0yravTaFE0rOc0pxF5l4PA1FCVmtjOyYZgj7JUeA +zPKUtgIPz28SUzsFdw== +-----END CERTIFICATE----- diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada-async.docker.yml b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada-async.docker.yml new file mode 100644 index 000000000..524cadb34 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada-async.docker.yml @@ -0,0 +1,905 @@ +auth: + enabled: true + global: false + claims: + role: http://schemas.microsoft.com/ws/2008/06/identity/claims/role + +http: + retries: 2 + interval: 2.0 + exponential: true + +useForwardedHeaders: true +passQueryString: true +forwardRequestHeaders: true +forwardResponseHeaders: true +generateRequestId: true +generateTraceId: true +useLocalUrl: false +loadBalancer: + enabled: false + url: fabio:9999 + +extensions: + customErrors: + includeExceptionMessage: true + + cors: + allowCredentials: true + allowedOrigins: + - '*' + allowedMethods: + - post + - put + - delete + allowedHeaders: + - '*' + exposedHeaders: + - Request-ID + - Resource-ID + - Trace-ID + - Total-Count + + jwt: + issuerSigningKey: Gtn9vBDB5RCDLJSMqZQQmN75J8hgzbQwWkcD8jMIXnvCLAmlL0QVacUAbyootWihMrPIz + validIssuer: minispace + validateAudience: false + validateIssuer: true + validateLifetime: true + + swagger: + name: MiniSpace + reDocEnabled: false + title: MiniSpace API + version: v1 + routePrefix: docs + includeSecurity: true + + rabbitmq: + enabled: true + connectionName: api-gateway + hostnames: + - rabbitmq + port: 5672 + virtualHost: / + username: guest + password: guest + requestedConnectionTimeout: 3000 + socketReadTimeout: 3000 + socketWriteTimeout: 3000 + requestedHeartbeat: 60 + exchange: + declareExchange: true + durable: true + autoDelete: false + type: topic + messageContext: + enabled: true + header: message_context + logger: + enabled: true + spanContextHeader: span_context + + tracing: + serviceName: api-gateway + udpHost: jaeger + udpPort: 6831 + maxPacketSize: 0 + sampler: const + useEmptyTracer: false + +modules: + home: + routes: + - upstream: / + method: GET + use: return_value + returnValue: Welcome to MiniSpace API [async]! + + identity: + path: /identity + routes: + - upstream: /users/{userId} + method: GET + use: downstream + downstream: identity-service/users/{userId} + auth: true + claims: + role: admin + + - upstream: /me + method: GET + use: downstream + downstream: identity-service/me + auth: true + + - upstream: /sign-up + method: POST + use: downstream + downstream: identity-service/sign-up + auth: false + resourceId: + property: userId + generate: true + + - upstream: /sign-in + method: POST + use: downstream + downstream: identity-service/sign-in + auth: false + + - upstream: /users/{userId}/ban + method: POST + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /users/{userId}/ban + method: DELETE + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /password/forgot + method: POST + use: downstream + downstream: identity-service/password/forgot + auth: false + + - upstream: /password/reset + method: POST + use: downstream + downstream: identity-service/password/reset + auth: false + + - upstream: /email/verify + method: POST + use: downstream + downstream: identity-service/email/verify + auth: false + + - upstream: /2fa/enable + method: POST + use: downstream + downstream: identity-service/2fa/enable + auth: true + + - upstream: /2fa/disable + method: POST + use: downstream + downstream: identity-service/2fa/disable + auth: true + + - upstream: /2fa/generate-secret + method: POST + use: downstream + downstream: identity-service/2fa/generate-secret + auth: true + + - upstream: /2fa/verify-code + method: POST + use: downstream + downstream: identity-service/2fa/verify-code + auth: false + + - upstream: /access-tokens/revoke + method: POST + use: downstream + downstream: identity-service/access-tokens/revoke + auth: true + + - upstream: /refresh-tokens/use + method: POST + use: downstream + downstream: identity-service/refresh-tokens/use + auth: true + + - upstream: /refresh-tokens/revoke + method: POST + use: downstream + downstream: identity-service/refresh-tokens/revoke + auth: true + + services: + identity-service: + localUrl: localhost:5004 + url: identity-service + + + + reports: + path: /reports + routes: + - upstream: / + method: POST + use: downstream + downstream: reports-service/reports + auth: true + + - upstream: /{reportId} + method: DELETE + use: downstream + downstream: reports-service/reports/{reportId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: reports-service/reports/search + auth: true + + - upstream: /{reportId}/cancel + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/cancel + auth: true + + - upstream: /{reportId}/start-review + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/start-review + auth: true + + - upstream: /{reportId}/resolve + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/resolve + auth: true + + - upstream: /{reportId}/reject + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/reject + auth: true + + - upstream: /students/{studentId} + method: GET + use: downstream + downstream: reports-service/reports/students/{studentId} + auth: true + + + services: + reports-service: + localUrl: localhost:5005 + url: reports-service + + + + notifications: + path: /notifications + routes: + - upstream: / + method: POST + use: downstream + downstream: notifications-service/notifications + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId} + auth: true + + - upstream: /notification/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/notification/{notificationId} + auth: true + + - upstream: /notification/{userId}/{notificationId} + method: DELETE + use: downstream + downstream: notifications-service/notifications/notification/{userId}/{notificationId} + auth: true + bind: + - userId:{userId} + - notificationId:{notificationId} + + - upstream: /{userId}/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId} + auth: true + description: Retrieves a specific notification for a user by notification ID. + + - upstream: /{userId}/{notificationId}/status + method: PUT + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId}/status + auth: true + description: Updates the status of a specific notification. + + - upstream: /notificationHub + method: GET + use: downstream + downstream: notifications-service/notificationHub + auth: false + + - upstream: /notificationHub/negotiate + method: POST + use: downstream + downstream: notifications-service/notificationHub/negotiate + auth: false + + services: + notifications-service: + localUrl: localhost:5006 + url: notifications-service + + + students: + path: /students + routes: + - upstream: / + method: GET + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId} + method: GET + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: /{studentId}/settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + + - upstream: /{studentId}/gallery + method: GET + use: downstream + downstream: students-service/students/{studentId}/gallery + auth: true + + - upstream: /{studentId}/visibility-settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/visibility-settings + auth: true + + - upstream: /{studentId}/events + method: GET + use: downstream + downstream: students-service/students/{studentId}/events + auth: true + + - upstream: /{studentId}/notifications + method: GET + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + - upstream: /{studentId} + method: PUT + use: downstream + downstream: students-service/students/{studentId} + bind: + - studentId:{studentId} + auth: true + + - upstream: /{studentId}/settings + method: PUT + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + + - upstream: /{studentId}/state/{state} + method: PUT + use: downstream + downstream: students-service/students/{studentId}/state/{state} + bind: + - studentId:{studentId} + - state:{state} + auth: true + + - upstream: /{studentId} + method: DELETE + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: / + method: POST + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId}/notifications + method: POST + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + services: + students-service: + localUrl: localhost:5007 + url: students-service + + + + events: + path: /events + routes: + - upstream: / + method: POST + use: downstream + downstream: events-service/events + auth: true + + - upstream: /{eventId} + method: PUT + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /{eventId} + method: GET + use: downstream + downstream: events-service/events/{eventId} + + - upstream: /student/{studentId} + method: GET + use: downstream + downstream: events-service/events/student/{studentId} + auth: true + + - upstream: /{eventId} + method: DELETE + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: events-service/events/search + + - upstream: /search/organizer + method: POST + use: downstream + downstream: events-service/events/search/organizer + auth: true + + - upstream: /{eventId}/show-interest + method: POST + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/show-interest + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/sign-up + method: POST + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/sign-up + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/rate + method: POST + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rate + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rating + method: GET + use: downstream + downstream: events-service/events/{eventId}/rating + + - upstream: /organizer/{organizerId} + method: GET + use: downstream + downstream: events-service/events/organizer/{organizerId} + auth: true + + - upstream: /{eventId}/participants + method: GET + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: POST + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + services: + events-service: + localUrl: localhost:5008 + url: events-service + + + + comments: + path: /comments + routes: + - upstream: / + method: POST + use: downstream + downstream: comments-service/comments + auth: true + + - upstream: /{commentId} + method: PUT + use: downstream + downstream: comments-service/comments/{commentId} + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId} + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId} + auth: true + + - upstream: /{commentId}/like + method: POST + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId}/like + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: comments-service/comments/search + + - upstream: /{commentId} + method: GET + use: downstream + downstream: comments-service/comments/{commentId} + + services: + comments-service: + localUrl: localhost:5009 + url: comments-service + + + + reactions: + path: /reactions + routes: + - upstream: / + method: POST + use: downstream + downstream: reactions-service/reactions + auth: true + + - upstream: /{reactionId} + method: DELETE + use: downstream + downstream: reactions-service/reactions/{reactionId} + auth: true + + - upstream: / + method: GET + use: downstream + downstream: reactions-service/reactions + + - upstream: /summary + method: GET + use: downstream + downstream: reactions-service/reactions/summary + + services: + reactions-service: + localUrl: localhost:5010 + url: reactions-service + + + + statistics: + path: /statistics + routes: + - upstream: / + method: GET + use: downstream + downstream: statistics-service/statistics + auth: true + + - upstream: /rating + method: GET + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + - upstream: /rating + method: POST + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + services: + statistics-service: + localUrl: localhost:5011 + url: statistics-service + + + + friends: + path: /friends + routes: + - upstream: / + method: GET + use: downstream + downstream: friends-service/friends + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: friends-service/friends/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /{userId}/invite + method: POST + use: downstream + downstream: friends-service/friends/{userId}/invite + bind: + - userId: {userId} + auth: true + afterDispatch: + - use: publish + event: FriendInviteSent + target: notifications-service/events + routingKey: friend_request_created + + - upstream: /{requesterId}/{friendId}/remove + method: DELETE + use: downstream + downstream: friends-service/friends/{requesterId}/{friendId}/remove + bind: + - friendId: {friendId} + - requesterId: {requesterId} + auth: true + + - upstream: /requests/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/accept + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/accept + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/decline + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/decline + bind: + - userId: {userId} + auth: true + + - upstream: /pending + method: GET + use: downstream + downstream: friends-service/friends/pending + auth: true + + - upstream: /pending/all + method: GET + use: downstream + downstream: friends-service/friends/pending/all + auth: true + + - upstream: /requests/sent/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/sent/{userId} + auth: true + + - upstream: /requests/{userId}/withdraw + method: PUT + use: downstream + downstream: friends-service/friends/requests/{userId}/withdraw + bind: + - userId: {userId} + auth: true + + services: + friends-service: + localUrl: localhost:5012 + url: friends-service + + + + posts: + path: /posts + routes: + - upstream: / + method: POST + use: downstream + downstream: posts-service/posts + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: posts-service/posts/search + auth: true + + - upstream: /{postId} + method: PUT + use: downstream + downstream: posts-service/posts/{postId} + bind: + - postId:{postId} + auth: true + + - upstream: /{postId}/state/{state} + method: PUT + use: downstream + downstream: posts-service/posts/{postId}/state/{state} + bind: + - postId:{postId} + - state:{state} + auth: true + + - upstream: /{postId} + method: GET + use: downstream + downstream: posts-service/posts/{postId} + + - upstream: / + method: GET + use: downstream + downstream: posts-service/posts + + - upstream: /{postId} + method: DELETE + use: downstream + downstream: posts-service/posts/{postId} + auth: true + + services: + posts-service: + localUrl: localhost:5013 + url: posts-service + + + + mediafiles: + path: /media-files + routes: + - upstream: / + method: POST + use: downstream + downstream: mediafiles-service/media-files + auth: true + + - upstream: /{mediaFileId} + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId} + + - upstream: /{mediaFileId}/original + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId}/original + + - upstream: /delete/{mediaFileUrl} + method: DELETE + use: downstream + downstream: mediafiles-service/media-files/delete/{mediaFileUrl} + auth: true + + services: + mediafiles-service: + localUrl: localhost:5014 + url: mediafiles-service + + + + organizations: + path: /organizations + routes: + - upstream: / + method: POST + use: downstream + downstream: organizations-service/organizations + auth: true + + - upstream: /{organizationId} + method: DELETE + use: downstream + downstream: organizations-service/organizations/{organizationId} + auth: true + + - upstream: /{organizationId}/children + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + auth: true + + - upstream: /{organizationId}/organizer + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/organizer + auth: true + + - upstream: /{organizationId}/organizer/{organizerId} + method: DELETE + use: downstream + downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId} + auth: true + + - upstream: /{organizationId} + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId} + + - upstream: /{organizationId}/details + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/details + + - upstream: /root + method: GET + use: downstream + downstream: organizations-service/organizations/root + + - upstream: /{organizationId}/children + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + + - upstream: /{organizationId}/children/all + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children/all + + - upstream: /organizer/{organizerId} + method: GET + use: downstream + downstream: organizations-service/organizations/organizer/{organizerId} + auth: true + + services: + organizations-service: + localUrl: localhost:5015 + url: organizations-service \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada-async.yml b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada-async.yml new file mode 100644 index 000000000..dc47eaf91 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada-async.yml @@ -0,0 +1,904 @@ +auth: + enabled: true + global: false + claims: + role: http://schemas.microsoft.com/ws/2008/06/identity/claims/role + +http: + retries: 2 + interval: 2.0 + exponential: true + +useForwardedHeaders: true +passQueryString: true +forwardRequestHeaders: true +forwardResponseHeaders: true +generateRequestId: true +generateTraceId: true +useLocalUrl: true +loadBalancer: + enabled: false + url: localhost:9999 + +extensions: + customErrors: + includeExceptionMessage: true + + cors: + allowCredentials: true + allowedOrigins: + - '*' + allowedMethods: + - post + - put + - delete + allowedHeaders: + - '*' + exposedHeaders: + - Request-ID + - Resource-ID + - Trace-ID + - Total-Count + + jwt: + issuerSigningKey: Gtn9vBDB5RCDLJSMqZQQmN75J8hgzbQwWkcD8jMIXnvCLAmlL0QVacUAbyootWihMrPIz + validIssuer: minispace + validateAudience: false + validateIssuer: true + validateLifetime: true + + swagger: + name: MiniSpace + reDocEnabled: false + title: MiniSpace API + version: v1 + routePrefix: docs + includeSecurity: true + + rabbitmq: + enabled: true + connectionName: api-gateway + hostnames: + - localhost + port: 5672 + virtualHost: / + username: guest + password: guest + requestedConnectionTimeout: 3000 + socketReadTimeout: 3000 + socketWriteTimeout: 3000 + requestedHeartbeat: 60 + exchange: + declareExchange: true + durable: true + autoDelete: false + type: topic + messageContext: + enabled: true + header: message_context + logger: + enabled: true + spanContextHeader: span_context + + tracing: + serviceName: api-gateway + udpHost: localhost + udpPort: 6831 + maxPacketSize: 0 + sampler: const + useEmptyTracer: false + +modules: + home: + routes: + - upstream: / + method: GET + use: return_value + returnValue: Welcome to MiniSpace API [async]! + + identity: + path: /identity + routes: + - upstream: /users/{userId} + method: GET + use: downstream + downstream: identity-service/users/{userId} + auth: true + claims: + role: admin + + - upstream: /me + method: GET + use: downstream + downstream: identity-service/me + auth: true + + - upstream: /sign-up + method: POST + use: downstream + downstream: identity-service/sign-up + auth: false + resourceId: + property: userId + generate: true + + - upstream: /sign-in + method: POST + use: downstream + downstream: identity-service/sign-in + auth: false + + - upstream: /users/{userId}/ban + method: POST + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /users/{userId}/ban + method: DELETE + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /password/forgot + method: POST + use: downstream + downstream: identity-service/password/forgot + auth: false + + - upstream: /password/reset + method: POST + use: downstream + downstream: identity-service/password/reset + auth: false + + - upstream: /email/verify + method: POST + use: downstream + downstream: identity-service/email/verify + auth: false + + - upstream: /2fa/enable + method: POST + use: downstream + downstream: identity-service/2fa/enable + auth: true + + - upstream: /2fa/disable + method: POST + use: downstream + downstream: identity-service/2fa/disable + auth: true + + - upstream: /2fa/generate-secret + method: POST + use: downstream + downstream: identity-service/2fa/generate-secret + auth: true + + - upstream: /2fa/verify-code + method: POST + use: downstream + downstream: identity-service/2fa/verify-code + auth: false + + - upstream: /access-tokens/revoke + method: POST + use: downstream + downstream: identity-service/access-tokens/revoke + auth: true + + - upstream: /refresh-tokens/use + method: POST + use: downstream + downstream: identity-service/refresh-tokens/use + auth: true + + - upstream: /refresh-tokens/revoke + method: POST + use: downstream + downstream: identity-service/refresh-tokens/revoke + auth: true + + services: + identity-service: + localUrl: localhost:5004 + url: identity-service + + + + reports: + path: /reports + routes: + - upstream: / + method: POST + use: downstream + downstream: reports-service/reports + auth: true + + - upstream: /{reportId} + method: DELETE + use: downstream + downstream: reports-service/reports/{reportId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: reports-service/reports/search + auth: true + + - upstream: /{reportId}/cancel + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/cancel + auth: true + + - upstream: /{reportId}/start-review + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/start-review + auth: true + + - upstream: /{reportId}/resolve + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/resolve + auth: true + + - upstream: /{reportId}/reject + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/reject + auth: true + + - upstream: /students/{studentId} + method: GET + use: downstream + downstream: reports-service/reports/students/{studentId} + auth: true + + services: + reports-service: + localUrl: localhost:5005 + url: reports-service + + + + notifications: + path: /notifications + routes: + - upstream: / + method: POST + use: downstream + downstream: notifications-service/notifications + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId} + auth: true + + - upstream: /notification/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/notification/{notificationId} + auth: true + + - upstream: /notification/{userId}/{notificationId} + method: DELETE + use: downstream + downstream: notifications-service/notifications/notification/{userId}/{notificationId} + auth: true + bind: + - userId:{userId} + - notificationId:{notificationId} + + - upstream: /{userId}/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId} + auth: true + description: Retrieves a specific notification for a user by notification ID. + + - upstream: /{userId}/{notificationId}/status + method: PUT + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId}/status + auth: true + description: Updates the status of a specific notification. + + - upstream: /notificationHub + method: GET + use: downstream + downstream: notifications-service/notificationHub + auth: false + + - upstream: /notificationHub/negotiate + method: POST + use: downstream + downstream: notifications-service/notificationHub/negotiate + auth: false + + services: + notifications-service: + localUrl: localhost:5006 + url: notifications-service + + + students: + path: /students + routes: + - upstream: / + method: GET + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId} + method: GET + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: /{studentId}/settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + + - upstream: /{studentId}/gallery + method: GET + use: downstream + downstream: students-service/students/{studentId}/gallery + auth: true + + - upstream: /{studentId}/visibility-settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/visibility-settings + auth: true + + - upstream: /{studentId}/events + method: GET + use: downstream + downstream: students-service/students/{studentId}/events + auth: true + + - upstream: /{studentId}/notifications + method: GET + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + - upstream: /{studentId} + method: PUT + use: downstream + downstream: students-service/students/{studentId} + bind: + - studentId:{studentId} + auth: true + + - upstream: /{studentId}/settings + method: PUT + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + + - upstream: /{studentId}/state/{state} + method: PUT + use: downstream + downstream: students-service/students/{studentId}/state/{state} + bind: + - studentId:{studentId} + - state:{state} + auth: true + + - upstream: /{studentId} + method: DELETE + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: / + method: POST + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId}/notifications + method: POST + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + services: + students-service: + localUrl: localhost:5007 + url: students-service + + + + events: + path: /events + routes: + - upstream: / + method: POST + use: downstream + downstream: events-service/events + auth: true + + - upstream: /{eventId} + method: PUT + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /{eventId} + method: GET + use: downstream + downstream: events-service/events/{eventId} + + - upstream: /student/{studentId} + method: GET + use: downstream + downstream: events-service/events/student/{studentId} + auth: true + + - upstream: /{eventId} + method: DELETE + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: events-service/events/search + + - upstream: /search/organizer + method: POST + use: downstream + downstream: events-service/events/search/organizer + auth: true + + - upstream: /{eventId}/show-interest + method: POST + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/show-interest + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/sign-up + method: POST + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/sign-up + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/rate + method: POST + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rate + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rating + method: GET + use: downstream + downstream: events-service/events/{eventId}/rating + + - upstream: /organizer/{organizerId} + method: GET + use: downstream + downstream: events-service/events/organizer/{organizerId} + auth: true + + - upstream: /{eventId}/participants + method: GET + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: POST + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + services: + events-service: + localUrl: localhost:5008 + url: events-service + + + + comments: + path: /comments + routes: + - upstream: / + method: POST + use: downstream + downstream: comments-service/comments + auth: true + + - upstream: /{commentId} + method: PUT + use: downstream + downstream: comments-service/comments/{commentId} + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId} + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId} + auth: true + + - upstream: /{commentId}/like + method: POST + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId}/like + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: comments-service/comments/search + + - upstream: /{commentId} + method: GET + use: downstream + downstream: comments-service/comments/{commentId} + + services: + comments-service: + localUrl: localhost:5009 + url: comments-service + + + + reactions: + path: /reactions + routes: + - upstream: / + method: POST + use: downstream + downstream: reactions-service/reactions + auth: true + + - upstream: /{reactionId} + method: DELETE + use: downstream + downstream: reactions-service/reactions/{reactionId} + auth: true + + - upstream: / + method: GET + use: downstream + downstream: reactions-service/reactions + + - upstream: /summary + method: GET + use: downstream + downstream: reactions-service/reactions/summary + + services: + reactions-service: + localUrl: localhost:5010 + url: reactions-service + + + + statistics: + path: /statistics + routes: + - upstream: / + method: GET + use: downstream + downstream: statistics-service/statistics + auth: true + + - upstream: /rating + method: GET + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + - upstream: /rating + method: POST + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + services: + statistics-service: + localUrl: localhost:5011 + url: statistics-service + + + + friends: + path: /friends + routes: + - upstream: / + method: GET + use: downstream + downstream: friends-service/friends + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: friends-service/friends/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /{userId}/invite + method: POST + use: downstream + downstream: friends-service/friends/{userId}/invite + bind: + - userId: {userId} + auth: true + afterDispatch: + - use: publish + event: FriendInviteSent + target: notifications-service/events + routingKey: friend_request_created + + - upstream: /{requesterId}/{friendId}/remove + method: DELETE + use: downstream + downstream: friends-service/friends/{requesterId}/{friendId}/remove + bind: + - friendId: {friendId} + - requesterId: {requesterId} + auth: true + + - upstream: /requests/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/accept + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/accept + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/decline + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/decline + bind: + - userId: {userId} + auth: true + + - upstream: /pending + method: GET + use: downstream + downstream: friends-service/friends/pending + auth: true + + - upstream: /pending/all + method: GET + use: downstream + downstream: friends-service/friends/pending/all + auth: true + + - upstream: /requests/sent/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/sent/{userId} + auth: true + + - upstream: /requests/{userId}/withdraw + method: PUT + use: downstream + downstream: friends-service/friends/requests/{userId}/withdraw + bind: + - userId: {userId} + auth: true + + services: + friends-service: + localUrl: localhost:5012 + url: friends-service + + + + posts: + path: /posts + routes: + - upstream: / + method: POST + use: downstream + downstream: posts-service/posts + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: posts-service/posts/search + auth: true + + - upstream: /{postId} + method: PUT + use: downstream + downstream: posts-service/posts/{postId} + bind: + - postId:{postId} + auth: true + + - upstream: /{postId}/state/{state} + method: PUT + use: downstream + downstream: posts-service/posts/{postId}/state/{state} + bind: + - postId:{postId} + - state:{state} + auth: true + + - upstream: /{postId} + method: GET + use: downstream + downstream: posts-service/posts/{postId} + + - upstream: / + method: GET + use: downstream + downstream: posts-service/posts + + - upstream: /{postId} + method: DELETE + use: downstream + downstream: posts-service/posts/{postId} + auth: true + + services: + posts-service: + localUrl: localhost:5013 + url: posts-service + + + + mediafiles: + path: /media-files + routes: + - upstream: / + method: POST + use: downstream + downstream: mediafiles-service/media-files + auth: true + + - upstream: /{mediaFileId} + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId} + + - upstream: /{mediaFileId}/original + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId}/original + + - upstream: /delete/{mediaFileUrl} + method: DELETE + use: downstream + downstream: mediafiles-service/media-files/delete/{mediaFileUrl} + auth: true + + services: + mediafiles-service: + localUrl: localhost:5014 + url: mediafiles-service + + + + organizations: + path: /organizations + routes: + - upstream: / + method: POST + use: downstream + downstream: organizations-service/organizations + auth: true + + - upstream: /{organizationId} + method: DELETE + use: downstream + downstream: organizations-service/organizations/{organizationId} + auth: true + + - upstream: /{organizationId}/children + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + auth: true + + - upstream: /{organizationId}/organizer + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/organizer + auth: true + + - upstream: /{organizationId}/organizer/{organizerId} + method: DELETE + use: downstream + downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId} + auth: true + + - upstream: /{organizationId} + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId} + + - upstream: /{organizationId}/details + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/details + + - upstream: /root + method: GET + use: downstream + downstream: organizations-service/organizations/root + + - upstream: /{organizationId}/children + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + + - upstream: /{organizationId}/children/all + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children/all + + - upstream: /organizer/{organizerId} + method: GET + use: downstream + downstream: organizations-service/organizations/organizer/{organizerId} + auth: true + + services: + organizations-service: + localUrl: localhost:5015 + url: organizations-service \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada.docker.yml b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada.docker.yml new file mode 100644 index 000000000..01d674117 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/ntrada.docker.yml @@ -0,0 +1,881 @@ +auth: + enabled: true + global: false + claims: + role: http://schemas.microsoft.com/ws/2008/06/identity/claims/role + +http: + retries: 2 + interval: 2.0 + exponential: true + +useForwardedHeaders: true +passQueryString: true +forwardRequestHeaders: true +forwardResponseHeaders: true +generateRequestId: true +generateTraceId: true +useLocalUrl: false +loadBalancer: + enabled: false + url: faibo:9999 + +extensions: + customErrors: + includeExceptionMessage: true + + cors: + allowCredentials: true + allowedOrigins: + - '*' + allowedMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + allowedHeaders: + - '*' + exposedHeaders: + - Request-ID + - Resource-ID + - Trace-ID + - Total-Count + + jwt: + issuerSigningKey: Gtn9vBDB5RCDLJSMqZQQmN75J8hgzbQwWkcD8jMIXnvCLAmlL0QVacUAbyootWihMrPIz + validIssuer: minispace + validateAudience: false + validateIssuer: false + validateLifetime: false + + swagger: + name: MiniSpace + reDocEnabled: false + title: MiniSpace API + version: v1 + routePrefix: docs + includeSecurity: true + + tracing: + serviceName: api-gateway + udpHost: jaeger + udpPort: 6831 + maxPacketSize: 0 + sampler: const + useEmptyTracer: false + +modules: + home: + routes: + - upstream: / + method: GET + use: return_value + returnValue: Welcome to MiniSpace API! + + identity: + path: /identity + routes: + - upstream: /users/{userId} + method: GET + use: downstream + downstream: identity-service/users/{userId} + auth: true + claims: + role: admin + + - upstream: /me + method: GET + use: downstream + downstream: identity-service/me + auth: true + + - upstream: /sign-up + method: POST + use: downstream + downstream: identity-service/sign-up + auth: false + resourceId: + property: userId + generate: true + + - upstream: /sign-in + method: POST + use: downstream + downstream: identity-service/sign-in + auth: false + + - upstream: /users/{userId}/ban + method: POST + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /users/{userId}/ban + method: DELETE + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /password/forgot + method: POST + use: downstream + downstream: identity-service/password/forgot + auth: false + + - upstream: /password/reset + method: POST + use: downstream + downstream: identity-service/password/reset + auth: false + + - upstream: /email/verify + method: POST + use: downstream + downstream: identity-service/email/verify + auth: false + + - upstream: /2fa/enable + method: POST + use: downstream + downstream: identity-service/2fa/enable + auth: true + + - upstream: /2fa/disable + method: POST + use: downstream + downstream: identity-service/2fa/disable + auth: true + + - upstream: /2fa/generate-secret + method: POST + use: downstream + downstream: identity-service/2fa/generate-secret + auth: true + + - upstream: /2fa/verify-code + method: POST + use: downstream + downstream: identity-service/2fa/verify-code + auth: false + + - upstream: /access-tokens/revoke + method: POST + use: downstream + downstream: identity-service/access-tokens/revoke + auth: true + + - upstream: /refresh-tokens/use + method: POST + use: downstream + downstream: identity-service/refresh-tokens/use + auth: true + + - upstream: /refresh-tokens/revoke + method: POST + use: downstream + downstream: identity-service/refresh-tokens/revoke + auth: true + + services: + identity-service: + localUrl: localhost:5004 + url: identity-service + + + + reports: + path: /reports + routes: + - upstream: / + method: POST + use: downstream + downstream: reports-service/reports + auth: true + + - upstream: /{reportId} + method: DELETE + use: downstream + downstream: reports-service/reports/{reportId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: reports-service/reports/search + auth: true + + - upstream: /{reportId}/cancel + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/cancel + auth: true + + - upstream: /{reportId}/start-review + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/start-review + auth: true + + - upstream: /{reportId}/resolve + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/resolve + auth: true + + - upstream: /{reportId}/reject + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/reject + auth: true + + - upstream: /students/{studentId} + method: GET + use: downstream + downstream: reports-service/reports/students/{studentId} + auth: true + + services: + reports-service: + localUrl: localhost:5005 + url: reports-service + + + + notifications: + path: /notifications + routes: + - upstream: / + method: POST + use: downstream + downstream: notifications-service/notifications + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId} + auth: true + + - upstream: /notification/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/notification/{notificationId} + auth: true + + - upstream: /notification/{userId}/{notificationId} + method: DELETE + use: downstream + downstream: notifications-service/notifications/notification/{userId}/{notificationId} + auth: true + bind: + - userId:{userId} + - notificationId:{notificationId} + + - upstream: /{userId}/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId} + auth: true + description: Retrieves a specific notification for a user by notification ID. + + - upstream: /{userId}/{notificationId}/status + method: PUT + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId}/status + auth: true + description: Updates the status of a specific notification. + + - upstream: /notificationHub + method: GET + use: downstream + downstream: notifications-service/notificationHub + auth: false + + - upstream: /notificationHub/negotiate + method: POST + use: downstream + downstream: notifications-service/notificationHub/negotiate + auth: false + + services: + notifications-service: + localUrl: localhost:5006 + url: notifications-service + + + students: + path: /students + routes: + - upstream: / + method: GET + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId} + method: GET + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: /{studentId}/settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + + - upstream: /{studentId}/gallery + method: GET + use: downstream + downstream: students-service/students/{studentId}/gallery + auth: true + + - upstream: /{studentId}/visibility-settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/visibility-settings + auth: true + + - upstream: /{studentId}/events + method: GET + use: downstream + downstream: students-service/students/{studentId}/events + auth: true + + - upstream: /{studentId}/notifications + method: GET + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + - upstream: /{studentId} + method: PUT + use: downstream + downstream: students-service/students/{studentId} + bind: + - studentId:{studentId} + auth: true + + - upstream: /{studentId}/settings + method: PUT + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + + - upstream: /{studentId}/state/{state} + method: PUT + use: downstream + downstream: students-service/students/{studentId}/state/{state} + bind: + - studentId:{studentId} + - state:{state} + auth: true + + - upstream: /{studentId} + method: DELETE + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: / + method: POST + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId}/notifications + method: POST + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + services: + students-service: + localUrl: localhost:5007 + url: students-service + + + + events: + path: /events + routes: + - upstream: / + method: POST + use: downstream + downstream: events-service/events + auth: true + + - upstream: /{eventId} + method: PUT + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /{eventId} + method: GET + use: downstream + downstream: events-service/events/{eventId} + + - upstream: /student/{studentId} + method: GET + use: downstream + downstream: events-service/events/student/{studentId} + auth: true + + - upstream: /{eventId} + method: DELETE + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: events-service/events/search + + - upstream: /search/organizer + method: POST + use: downstream + downstream: events-service/events/search/organizer + auth: true + + - upstream: /{eventId}/show-interest + method: POST + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/show-interest + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/sign-up + method: POST + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/sign-up + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/rate + method: POST + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rate + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rating + method: GET + use: downstream + downstream: events-service/events/{eventId}/rating + + - upstream: /organizer/{organizerId} + method: GET + use: downstream + downstream: events-service/events/organizer/{organizerId} + auth: true + + - upstream: /{eventId}/participants + method: GET + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: POST + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + services: + events-service: + localUrl: localhost:5008 + url: events-service + + + + comments: + path: /comments + routes: + - upstream: / + method: POST + use: downstream + downstream: comments-service/comments + auth: true + + - upstream: /{commentId} + method: PUT + use: downstream + downstream: comments-service/comments/{commentId} + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId} + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId} + auth: true + + - upstream: /{commentId}/like + method: POST + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId}/like + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: comments-service/comments/search + + - upstream: /{commentId} + method: GET + use: downstream + downstream: comments-service/comments/{commentId} + + services: + comments-service: + localUrl: localhost:5009 + url: comments-service + + + + reactions: + path: /reactions + routes: + - upstream: / + method: POST + use: downstream + downstream: reactions-service/reactions + auth: true + + - upstream: /{reactionId} + method: DELETE + use: downstream + downstream: reactions-service/reactions/{reactionId} + auth: true + + - upstream: / + method: GET + use: downstream + downstream: reactions-service/reactions + + - upstream: /summary + method: GET + use: downstream + downstream: reactions-service/reactions/summary + + services: + reactions-service: + localUrl: localhost:5010 + url: reactions-service + + + + statistics: + path: /statistics + routes: + - upstream: / + method: GET + use: downstream + downstream: statistics-service/statistics + auth: true + + - upstream: /rating + method: GET + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + - upstream: /rating + method: POST + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + services: + statistics-service: + localUrl: localhost:5011 + url: statistics-service + + + + friends: + path: /friends + routes: + - upstream: / + method: GET + use: downstream + downstream: friends-service/friends + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: friends-service/friends/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /{userId}/invite + method: POST + use: downstream + downstream: friends-service/friends/{userId}/invite + bind: + - userId: {userId} + auth: true + afterDispatch: + - use: publish + event: FriendInviteSent + target: notifications-service/events + routingKey: friend_request_created + + - upstream: /{requesterId}/{friendId}/remove + method: DELETE + use: downstream + downstream: friends-service/friends/{requesterId}/{friendId}/remove + bind: + - friendId: {friendId} + - requesterId: {requesterId} + auth: true + + - upstream: /requests/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/accept + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/accept + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/decline + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/decline + bind: + - userId: {userId} + auth: true + + - upstream: /pending + method: GET + use: downstream + downstream: friends-service/friends/pending + auth: true + + - upstream: /pending/all + method: GET + use: downstream + downstream: friends-service/friends/pending/all + auth: true + + - upstream: /requests/sent/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/sent/{userId} + auth: true + + - upstream: /requests/{userId}/withdraw + method: PUT + use: downstream + downstream: friends-service/friends/requests/{userId}/withdraw + bind: + - userId: {userId} + auth: true + + services: + friends-service: + localUrl: localhost:5012 + url: friends-service + + + + posts: + path: /posts + routes: + - upstream: / + method: POST + use: downstream + downstream: posts-service/posts + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: posts-service/posts/search + auth: true + + - upstream: /{postId} + method: PUT + use: downstream + downstream: posts-service/posts/{postId} + bind: + - postId:{postId} + auth: true + + - upstream: /{postId}/state/{state} + method: PUT + use: downstream + downstream: posts-service/posts/{postId}/state/{state} + bind: + - postId:{postId} + - state:{state} + auth: true + + - upstream: /{postId} + method: GET + use: downstream + downstream: posts-service/posts/{postId} + + - upstream: / + method: GET + use: downstream + downstream: posts-service/posts + + - upstream: /{postId} + method: DELETE + use: downstream + downstream: posts-service/posts/{postId} + auth: true + + services: + posts-service: + localUrl: localhost:5013 + url: posts-service + + + + mediafiles: + path: /media-files + routes: + - upstream: / + method: POST + use: downstream + downstream: mediafiles-service/media-files + auth: true + + - upstream: /{mediaFileId} + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId} + + - upstream: /{mediaFileId}/original + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId}/original + + - upstream: /delete/{mediaFileUrl} + method: DELETE + use: downstream + downstream: mediafiles-service/media-files/delete/{mediaFileUrl} + auth: true + + services: + mediafiles-service: + localUrl: localhost:5014 + url: mediafiles-service + + + + organizations: + path: /organizations + routes: + - upstream: / + method: POST + use: downstream + downstream: organizations-service/organizations + auth: true + + - upstream: /{organizationId} + method: DELETE + use: downstream + downstream: organizations-service/organizations/{organizationId} + auth: true + + - upstream: /{organizationId}/children + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + auth: true + + - upstream: /{organizationId}/organizer + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/organizer + auth: true + + - upstream: /{organizationId}/organizer/{organizerId} + method: DELETE + use: downstream + downstream: organizations-service/organizations/{organizationId}/organizer/{organizerId} + auth: true + + - upstream: /{organizationId} + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId} + + - upstream: /{organizationId}/details + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/details + + - upstream: /root + method: GET + use: downstream + downstream: organizations-service/organizations/root + + - upstream: /{organizationId}/children + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + + - upstream: /{organizationId}/children/all + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children/all + + - upstream: /organizer/{organizerId} + method: GET + use: downstream + downstream: organizations-service/organizations/organizer/{organizerId} + auth: true + + services: + organizations-service: + localUrl: localhost:5015 + url: organizations-service \ No newline at end of file diff --git a/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/nuar.yml b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/nuar.yml new file mode 100644 index 000000000..d858af2f9 --- /dev/null +++ b/MiniSpace.APIGateway.Nuar/src/MiniSpace.APIGateway/nuar.yml @@ -0,0 +1,1263 @@ +auth: + enabled: true + global: false + claims: + role: http://schemas.microsoft.com/ws/2008/06/identity/claims/role + +http: + retries: 2 + interval: 2.0 + exponential: true + +useForwardedHeaders: true +passQueryString: true +forwardRequestHeaders: true +forwardResponseHeaders: true +generateRequestId: true +generateTraceId: true +useLocalUrl: true +loadBalancer: + enabled: false + url: fabio:9999 + +extensions: + customErrors: + includeExceptionMessage: true + + cors: + allowCredentials: true + allowedOrigins: + - 'http://localhost:5606' + allowedMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + allowedHeaders: + - '*' + exposedHeaders: + - Request-ID + - Resource-ID + - Trace-ID + - Total-Count + + jwt: + issuerSigningKey: Gtn9vBDB5RCDLJSMqZQQmN75J8hgzbQwWkcD8jMIXnvCLAmlL0QVacUAbyootWihMrPIz + validIssuer: minispace + validateAudience: false + validateIssuer: false + validateLifetime: false + + swagger: + name: MiniSpace + reDocEnabled: false + title: MiniSpace API + version: v1 + routePrefix: docs + includeSecurity: true + + tracing: + serviceName: api-gateway + udpHost: localhost + udpPort: 6831 + maxPacketSize: 0 + sampler: const + useEmptyTracer: false + +modules: + home: + routes: + - upstream: / + method: GET + use: return_value + returnValue: Welcome to MiniSpace API! + + identity: + path: /identity + routes: + - upstream: /users/{userId} + method: GET + use: downstream + downstream: identity-service/users/{userId} + auth: true + claims: + role: admin + + - upstream: /me + method: GET + use: downstream + downstream: identity-service/me + auth: true + + - upstream: /sign-up + method: POST + use: downstream + downstream: identity-service/sign-up + auth: false + resourceId: + property: userId + generate: true + + - upstream: /sign-in + method: POST + use: downstream + downstream: identity-service/sign-in + auth: false + + - upstream: /users/{userId}/ban + method: POST + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /users/{userId}/ban + method: DELETE + use: downstream + downstream: identity-service/users/{userId}/ban + auth: true + + - upstream: /password/forgot + method: POST + use: downstream + downstream: identity-service/password/forgot + auth: false + + - upstream: /password/reset + method: POST + use: downstream + downstream: identity-service/password/reset + auth: false + + - upstream: /email/verify + method: POST + use: downstream + downstream: identity-service/email/verify + auth: false + + - upstream: /2fa/enable + method: POST + use: downstream + downstream: identity-service/2fa/enable + auth: true + + - upstream: /2fa/disable + method: POST + use: downstream + downstream: identity-service/2fa/disable + auth: true + + - upstream: /2fa/generate-secret + method: POST + use: downstream + downstream: identity-service/2fa/generate-secret + auth: true + + - upstream: /2fa/verify-code + method: POST + use: downstream + downstream: identity-service/2fa/verify-code + auth: false + + - upstream: /access-tokens/revoke + method: POST + use: downstream + downstream: identity-service/access-tokens/revoke + auth: false + + - upstream: /refresh-tokens/use + method: POST + use: downstream + downstream: identity-service/refresh-tokens/use + auth: false + + - upstream: /refresh-tokens/revoke + method: POST + use: downstream + downstream: identity-service/refresh-tokens/revoke + auth: false + + - upstream: /users/status + method: PUT + use: downstream + downstream: identity-service/users/status + auth: false + + services: + identity-service: + localUrl: localhost:5004 + url: identity-service + + + + reports: + path: /reports + routes: + - upstream: / + method: POST + use: downstream + downstream: reports-service/reports + auth: true + + - upstream: /{reportId} + method: DELETE + use: downstream + downstream: reports-service/reports/{reportId} + auth: true + + - upstream: /search + method: POST + use: downstream + downstream: reports-service/reports/search + auth: true + + - upstream: /{reportId}/cancel + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/cancel + auth: true + + - upstream: /{reportId}/start-review + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/start-review + auth: true + + - upstream: /{reportId}/resolve + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/resolve + auth: true + + - upstream: /{reportId}/reject + method: POST + use: downstream + downstream: reports-service/reports/{reportId}/reject + auth: true + + - upstream: /students/{studentId} + method: GET + use: downstream + downstream: reports-service/reports/students/{studentId} + auth: true + + services: + reports-service: + localUrl: localhost:5005 + url: reports-service + + + + notifications: + path: /notifications + routes: + - upstream: / + method: POST + use: downstream + downstream: notifications-service/notifications + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId} + auth: true + + - upstream: /notification/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/notification/{notificationId} + auth: true + + - upstream: /notification/{userId}/{notificationId} + method: DELETE + use: downstream + downstream: notifications-service/notifications/notification/{userId}/{notificationId} + auth: true + bind: + - userId:{userId} + - notificationId:{notificationId} + + - upstream: /{userId}/{notificationId} + method: GET + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId} + auth: true + description: Retrieves a specific notification for a user by notification ID. + + - upstream: /{userId}/{notificationId}/status + method: PUT + use: downstream + downstream: notifications-service/notifications/{userId}/{notificationId}/status + auth: true + description: Updates the status of a specific notification. + + # - upstream: /notificationHub + # method: GET + # use: downstream + # downstream: notifications-service/notificationHub + # auth: false + + - upstream: /notificationHub/negotiate + method: POST + use: downstream + downstream: notifications-service/notificationHub/negotiate + auth: false + + services: + notifications-service: + localUrl: localhost:5006 + url: notifications-service + + + communication: + path: /communication + routes: + - upstream: /chats + method: POST + use: downstream + downstream: communication-service/communication/chats + auth: true + + - upstream: /chats/{chatId}/messages + method: POST + use: downstream + downstream: communication-service/communication/chats/{chatId}/messages + auth: true + bind: + - chatId:{chatId} + + - upstream: /chats/{chatId}/users + method: PUT + use: downstream + downstream: communication-service/communication/chats/{chatId}/users + auth: true + bind: + - chatId:{chatId} + + - upstream: /chats/{chatId}/messages/{messageId}/status + method: PUT + use: downstream + downstream: communication-service/communication/chats/{chatId}/messages/{messageId}/status + auth: true + bind: + - chatId:{chatId} + - messageId:{messageId} + + - upstream: /chats/user/{userId} + method: GET + use: downstream + downstream: communication-service/communication/chats/user/{userId} + auth: true + bind: + - userId:{userId} + + - upstream: /chats/{chatId} + method: GET + use: downstream + downstream: communication-service/communication/chats/{chatId} + auth: true + bind: + - chatId:{chatId} + + - upstream: /chats/{chatId}/messages + method: GET + use: downstream + downstream: communication-service/communication/chats/{chatId}/messages + auth: true + bind: + - chatId:{chatId} + + - upstream: /chats/{chatId}/{userId} + method: DELETE + use: downstream + downstream: communication-service/communication/chats/{chatId}/{userId} + auth: true + bind: + - chatId:{chatId} + - userId:{userId} + + - upstream: /chats/{chatId}/messages/{messageId} + method: DELETE + use: downstream + downstream: communication-service/communication/chats/{chatId}/messages/{messageId} + auth: true + bind: + - chatId:{chatId} + - messageId:{messageId} + + services: + communication-service: + localUrl: localhost:5016 + url: communication-service + + + students: + path: /students + routes: + - upstream: / + method: GET + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId} + method: GET + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: /{studentId}/settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + + - upstream: /{studentId}/gallery + method: GET + use: downstream + downstream: students-service/students/{studentId}/gallery + auth: true + + - upstream: /{studentId}/visibility-settings + method: GET + use: downstream + downstream: students-service/students/{studentId}/visibility-settings + auth: true + + - upstream: /{studentId}/events + method: GET + use: downstream + downstream: students-service/students/{studentId}/events + auth: true + + - upstream: /{studentId}/notifications + method: GET + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + - upstream: /profiles/users/{userId}/views/paginated + method: GET + use: downstream + downstream: students-service/students/profiles/users/{userId}/views/paginated + auth: true + bind: + - userId:{userId} + + - upstream: /profiles/users/{userId}/views/viewed + method: GET + use: downstream + downstream: students-service/students/profiles/users/{userId}/views/viewed + auth: true + bind: + - userId:{userId} + + - upstream: /{blockerId}/blocked-users + method: GET + use: downstream + downstream: students-service/students/{blockerId}/blocked-users + auth: true + bind: + - blockerId:{blockerId} + + - upstream: /{studentId} + method: PUT + use: downstream + downstream: students-service/students/{studentId} + bind: + - studentId:{studentId} + auth: true + + - upstream: /{studentId}/settings + method: PUT + use: downstream + downstream: students-service/students/{studentId}/settings + auth: true + bind: + - studentId:{studentId} + + - upstream: /{studentId}/state/{state} + method: PUT + use: downstream + downstream: students-service/students/{studentId}/state/{state} + bind: + - studentId:{studentId} + - state:{state} + auth: true + + - upstream: /{studentId} + method: DELETE + use: downstream + downstream: students-service/students/{studentId} + auth: true + + - upstream: / + method: POST + use: downstream + downstream: students-service/students + auth: true + + - upstream: /{studentId}/notifications + method: POST + use: downstream + downstream: students-service/students/{studentId}/notifications + auth: true + + - upstream: /profiles/users/{userProfileId}/view + method: POST + use: downstream + downstream: students-service/students/profiles/users/{userProfileId}/view + auth: true + bind: + - userProfileId:{userProfileId} + + - upstream: /{blockerId}/block-user/{blockedUserId} + method: POST + use: downstream + downstream: students-service/students/{blockerId}/block-user/{blockedUserId} + auth: true + bind: + - blockerId:{blockerId} + - blockedUserId:{blockedUserId} + + - upstream: /{blockerId}/unblock-user/{blockedUserId} + method: POST + use: downstream + downstream: students-service/students/{blockerId}/unblock-user/{blockedUserId} + auth: true + bind: + - blockerId:{blockerId} + - blockedUserId:{blockedUserId} + + - upstream: /{studentId}/languages-and-interests + method: PUT + use: downstream + downstream: students-service/students/{studentId}/languages-and-interests + bind: + - studentId:{studentId} + auth: true + + services: + students-service: + localUrl: localhost:5007 + url: students-service + + + + events: + path: /events + routes: + - upstream: / + method: POST + use: downstream + downstream: events-service/events + auth: true + + - upstream: /{eventId} + method: PUT + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /{eventId} + method: GET + use: downstream + downstream: events-service/events/{eventId} + auth: true + + - upstream: /paginated + method: GET + use: downstream + downstream: events-service/events/paginated + + - upstream: /organizer/{organizerId}/paginated + method: GET + use: downstream + downstream: events-service/events/organizer/{organizerId}/paginated + bind: + - organizerId:{organizerId} + + - upstream: /users/{userId} + method: GET + use: downstream + downstream: events-service/events/users/{userId} + auth: true + bind: + - userId:{userId} + + - upstream: /student/{studentId} + method: GET + use: downstream + downstream: events-service/events/student/{studentId} + auth: true + + - upstream: /users/{userId}/feed + method: GET + use: downstream + downstream: events-service/events/users/{userId}/feed + auth: true + bind: + - userId:{userId} + + - upstream: /users/{userId}/views/paginated + method: GET + use: downstream + downstream: events-service/events/users/{userId}/views/paginated + auth: true + bind: + - userId:{userId} + + + - upstream: /{eventId} + method: DELETE + use: downstream + downstream: events-service/events/{eventId} + auth: true + + # - upstream: /search + # method: POST + # use: downstream + # downstream: events-service/events/search + # tesotwy with get + + - upstream: /search + method: GET + use: downstream + downstream: events-service/events/search + + - upstream: /search/organizer + method: POST + use: downstream + downstream: events-service/events/search/organizer + auth: true + + - upstream: /{eventId}/show-interest + method: POST + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/view + method: POST + use: downstream + downstream: events-service/events/{eventId}/view + auth: true + bind: + - eventId:{eventId} + + - upstream: /{eventId}/show-interest + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/show-interest + auth: true + + - upstream: /{eventId}/sign-up + method: POST + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/sign-up + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/sign-up + auth: true + + - upstream: /{eventId}/rate + method: POST + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rate + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/rate + auth: true + + - upstream: /{eventId}/rating + method: GET + use: downstream + downstream: events-service/events/{eventId}/rating + + - upstream: /organizer/{organizerId} + method: GET + use: downstream + downstream: events-service/events/organizer/{organizerId} + auth: true + + - upstream: /{eventId}/participants + method: GET + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: POST + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + - upstream: /{eventId}/participants + method: DELETE + use: downstream + downstream: events-service/events/{eventId}/participants + auth: true + + + services: + events-service: + localUrl: localhost:5008 + url: events-service + + + + comments: + path: /comments + routes: + - upstream: / + method: POST + use: downstream + downstream: comments-service/comments + auth: true + + - upstream: /{commentId} + method: PUT + use: downstream + downstream: comments-service/comments/{commentId} + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId} + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId} + auth: true + + - upstream: /{commentId}/like + method: POST + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /{commentId}/like + method: DELETE + use: downstream + downstream: comments-service/comments/{commentId}/like + bind: + - commentId:{commentId} + auth: true + + - upstream: /search + method: GET + use: downstream + downstream: comments-service/comments/search + + - upstream: /{commentId} + method: GET + use: downstream + downstream: comments-service/comments/{commentId} + + services: + comments-service: + localUrl: localhost:5009 + url: comments-service + + + + reactions: + path: /reactions + routes: + - upstream: / + method: POST + use: downstream + downstream: reactions-service/reactions + auth: true + + - upstream: /{reactionId} + method: PUT + use: downstream + downstream: reactions-service/reactions/{reactionId} + auth: true + bind: + - reactionId: {reactionId} + + - upstream: /{reactionId} + method: DELETE + use: downstream + downstream: reactions-service/reactions/{reactionId} + auth: true + bind: + - reactionId: {reactionId} + + - upstream: / + method: GET + use: downstream + downstream: reactions-service/reactions + + - upstream: /summary + method: GET + use: downstream + downstream: reactions-service/reactions/summary + + services: + reactions-service: + localUrl: localhost:5010 + url: reactions-service + + + + statistics: + path: /statistics + routes: + - upstream: / + method: GET + use: downstream + downstream: statistics-service/statistics + auth: true + + - upstream: /rating + method: GET + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + - upstream: /rating + method: POST + use: downstream + downstream: statistics-service/statistics/rating + auth: true + + services: + statistics-service: + localUrl: localhost:5011 + url: statistics-service + + + + friends: + path: /friends + routes: + - upstream: / + method: GET + use: downstream + downstream: friends-service/friends + auth: true + + - upstream: /{userId} + method: GET + use: downstream + downstream: friends-service/friends/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /{userId}/invite + method: POST + use: downstream + downstream: friends-service/friends/{userId}/invite + bind: + - userId: {userId} + auth: true + afterDispatch: + - use: publish + event: FriendInviteSent + target: notifications-service/events + routingKey: friend_request_created + + - upstream: /{requesterId}/{friendId}/remove + method: DELETE + use: downstream + downstream: friends-service/friends/{requesterId}/{friendId}/remove + bind: + - friendId: {friendId} + - requesterId: {requesterId} + auth: true + + - upstream: /requests/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/{userId} + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/accept + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/accept + bind: + - userId: {userId} + auth: true + + - upstream: /requests/{userId}/decline + method: POST + use: downstream + downstream: friends-service/friends/requests/{userId}/decline + bind: + - userId: {userId} + auth: true + + - upstream: /pending + method: GET + use: downstream + downstream: friends-service/friends/pending + auth: true + + - upstream: /pending/all + method: GET + use: downstream + downstream: friends-service/friends/pending/all + auth: true + + - upstream: /requests/sent/{userId} + method: GET + use: downstream + downstream: friends-service/friends/requests/sent/{userId} + auth: true + + - upstream: /{userId}/followers + method: GET + use: downstream + downstream: friends-service/friends/{userId}/followers + auth: true + bind: + - userId: {userId} + + - upstream: /{userId}/following + method: GET + use: downstream + downstream: friends-service/friends/{userId}/following + auth: true + bind: + - userId: {userId} + + - upstream: /requests/{userId}/withdraw + method: PUT + use: downstream + downstream: friends-service/friends/requests/{userId}/withdraw + bind: + - userId: {userId} + auth: true + + services: + friends-service: + localUrl: localhost:5012 + url: friends-service + + + + posts: + path: /posts + routes: + - upstream: / + method: POST + use: downstream + downstream: posts-service/posts + auth: true + + - upstream: /{originalPostId}/repost + method: POST + use: downstream + downstream: posts-service/posts/{originalPostId}/repost + auth: true + bind: + - originalPostId:{originalPostId} + + - upstream: /users/{userId}/feed + method: GET + use: downstream + downstream: posts-service/posts/users/{userId}/feed + bind: + - userId:{userId} + auth: true + + - upstream: /search + method: GET + use: downstream + downstream: posts-service/posts/search + auth: true + + - upstream: /{postId} + method: PUT + use: downstream + downstream: posts-service/posts/{postId} + bind: + - postId:{postId} + auth: true + + - upstream: /{postId}/state/{state} + method: PUT + use: downstream + downstream: posts-service/posts/{postId}/state/{state} + bind: + - postId:{postId} + - state:{state} + auth: true + + - upstream: /{postId} + method: GET + use: downstream + downstream: posts-service/posts/{postId} + bind: + - postId:{postId} + + - upstream: / + method: GET + use: downstream + downstream: posts-service/posts + + - upstream: /{postId} + method: DELETE + use: downstream + downstream: posts-service/posts/{postId} + auth: true + + services: + posts-service: + localUrl: localhost:5013 + url: posts-service + + + + mediafiles: + path: /media-files + routes: + - upstream: / + method: POST + use: downstream + downstream: mediafiles-service/media-files + auth: true + requestSizeLimit: 50_000_000 + formLimits: + multipartBodyLengthLimit: 50_000_000 + + - upstream: /files + method: POST + use: downstream + downstream: mediafiles-service/files + auth: true + requestSizeLimit: 50_000_000 + formLimits: + multipartBodyLengthLimit: 50_000_000 + + - upstream: /{mediaFileId} + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId} + + - upstream: /{mediaFileId}/original + method: GET + use: downstream + downstream: mediafiles-service/media-files/{mediaFileId}/original + + - upstream: /delete/{mediaFileUrl} + method: DELETE + use: downstream + downstream: mediafiles-service/media-files/delete/{mediaFileUrl} + auth: true + + services: + mediafiles-service: + localUrl: localhost:5014 + url: mediafiles-service + + + + organizations: + path: /organizations + routes: + - upstream: / + method: POST + use: downstream + downstream: organizations-service/organizations + auth: true + + - upstream: /{organizationId} + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId} + + - upstream: /{organizationId}/details + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/details + + - upstream: /root + method: GET + use: downstream + downstream: organizations-service/organizations/root + + - upstream: /{organizationId}/children + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + + - upstream: /{organizationId}/children/all + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/children/all + + - upstream: /users/{userId}/organizations + method: GET + use: downstream + downstream: organizations-service/organizations/users/{userId}/organizations + auth: true + bind: + - userId: {userId} + + - upstream: /{organizationId}/follow + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/follow + auth: true + bind: + - organizationId: {organizationId} + + - upstream: /users/{userId}/organizations/follow + method: GET + use: downstream + downstream: organizations-service/organizations/users/{userId}/organizations/follow + auth: true + bind: + - userId: {userId} + + + + - upstream: /{organizationId}/details/gallery-users + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/details/gallery-users + bind: + - organizationId: {organizationId} + + - upstream: /{organizationId}/roles + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/roles + auth: true + bind: + - organizationId: {organizationId} + + - upstream: /{organizationId}/requests + method: GET + use: downstream + downstream: organizations-service/organizations/{organizationId}/requests + bind: + - organizationId: {organizationId} + + - upstream: /paginated + method: GET + use: downstream + downstream: organizations-service/organizations/paginated + + - upstream: /{organizationId} + method: DELETE + use: downstream + downstream: organizations-service/organizations/{organizationId} + auth: true + + - upstream: /{organizationId}/children + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/children + auth: true + + - upstream: /{organizationId}/roles + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/roles + auth: true + + - upstream: /{organizationId}/follow + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/follow + auth: true + bind: + - organizationId: {organizationId} + + - upstream: /{organizationId}/leave + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/leave + auth: true + bind: + - organizationId: {organizationId} + + - upstream: /{organizationId}/requests/{requestId}/accept + method: PUT + use: downstream + downstream: organizations-service/organizations/{organizationId}/requests/{requestId}/accept + auth: true + bind: + - organizationId: {organizationId} + - requestId: {requestId} + + - upstream: /{organizationId}/requests/{requestId}/reject + method: PUT + use: downstream + downstream: organizations-service/organizations/{organizationId}/requests/{requestId}/reject + auth: true + bind: + - organizationId: {organizationId} + - requestId: {requestId} + + - upstream: /{organizationId}/roles/{roleId}/permissions + method: PUT + use: downstream + downstream: organizations-service/organizations/{organizationId}/roles/{roleId}/permissions + auth: true + + - upstream: /{organizationId}/invite + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/invite + auth: true + + - upstream: /{organizationId}/roles/{memberId} + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/roles/{memberId} + auth: true + + - upstream: /{organizationId}/privacy + method: POST + use: downstream + downstream: organizations-service/organizations/{organizationId}/privacy + auth: true + + - upstream: /{organizationId}/settings + method: PUT + use: downstream + downstream: organizations-service/organizations/{organizationId}/settings + auth: true + + - upstream: /{organizationId}/visibility + method: PUT + use: downstream + downstream: organizations-service/organizations/{organizationId}/visibility + auth: true + + - upstream: /{organizationId}/feed + method: PUT + use: downstream + downstream: organizations-service/organizations/{organizationId}/feed + auth: true + + - upstream: /{organizationId} + method: PUT + use: downstream + downstream: organizations-service/organizations/{organizationId} + auth: true + + services: + organizations-service: + localUrl: localhost:5015 + url: organizations-service + + diff --git a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml index dae0f3140..d858af2f9 100644 --- a/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml +++ b/MiniSpace.APIGateway/src/MiniSpace.APIGateway/ntrada.yml @@ -177,6 +177,12 @@ modules: downstream: identity-service/refresh-tokens/revoke auth: false + - upstream: /users/status + method: PUT + use: downstream + downstream: identity-service/users/status + auth: false + services: identity-service: localUrl: localhost:5004 @@ -954,6 +960,14 @@ modules: downstream: posts-service/posts auth: true + - upstream: /{originalPostId}/repost + method: POST + use: downstream + downstream: posts-service/posts/{originalPostId}/repost + auth: true + bind: + - originalPostId:{originalPostId} + - upstream: /users/{userId}/feed method: GET use: downstream diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/MiniSpace.Services.Comments.Api.csproj b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/MiniSpace.Services.Comments.Api.csproj index c66b12422..741fbf61b 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/MiniSpace.Services.Comments.Api.csproj +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/MiniSpace.Services.Comments.Api.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Program.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Program.cs index 60fe637b4..3e50da6b0 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Program.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Api/Program.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey; -using Convey.Secrets.Vault; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Secrets.Vault; +using Paralax.Logging; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -18,6 +17,7 @@ using MiniSpace.Services.Comments.Application.Services; using MiniSpace.Services.Comments.Infrastructure; using MiniSpace.Services.Comments.Core.Wrappers; +using Paralax.Core; namespace MiniSpace.Services.Identity.Api { @@ -27,7 +27,7 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/AddLike.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/AddLike.cs index 438a62314..37d9c8ff3 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/AddLike.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/AddLike.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Comments.Application.Commands { diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs index 51e700084..e82aa3dc9 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/CreateComment.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Comments.Application.Commands { diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs index 9d4e9cb01..699776b22 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteComment.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Comments.Application.Commands { diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteLike.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteLike.cs index 7d3061195..fca16ce3d 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteLike.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/DeleteLike.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Comments.Application.Commands { diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/AddLikeHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/AddLikeHandler.cs index 75ceb747f..313e57842 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/AddLikeHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/AddLikeHandler.cs @@ -1,13 +1,12 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Comments.Application.Events; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Application.Services.Clients; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; -using MiniSpace.Services.Comments.Core.Exceptions; using MiniSpace.Services.Comments.Application.Services; using System.Text.Json; @@ -43,8 +42,9 @@ public AddLikeHandler( public async Task HandleAsync(AddLike command, CancellationToken cancellationToken = default) { - var commandJson = JsonSerializer.Serialize(command, new JsonSerializerOptions { WriteIndented = true }); + var commandJson = JsonSerializer.Serialize(command, new JsonSerializerOptions { WriteIndented = true }); Console.WriteLine($"Received AddLike command: {commandJson}"); + var identity = _appContext.Identity; if (!identity.IsAuthenticated || identity.Id != command.UserId) @@ -72,7 +72,14 @@ public async Task HandleAsync(AddLike command, CancellationToken cancellationTok comment.Like(command.UserId); await UpdateCommentAsync(comment, commentContext); - await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId)); + await _messageBroker.PublishAsync(new LikeAdded( + commentId: command.CommentId, + userId: command.UserId, + commentContext: command.CommentContext, + likedAt: DateTime.UtcNow, + userName: $"{user.FirstName} {user.LastName}", + profileImageUrl: user.ProfileImageUrl + )); } private async Task GetCommentAsync(Guid commentId, CommentContext context) diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs index bacf66423..b62299c5b 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/CreateCommentHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Application.Events; using MiniSpace.Services.Comments.Application.Exceptions; @@ -49,15 +49,6 @@ public async Task HandleAsync(CreateComment command, CancellationToken cancellat { var identity = _appContext.Identity; - if (identity.IsAuthenticated) - { - Console.WriteLine($"User is authenticated: {identity.Id}"); - Console.WriteLine($"User command id is: {command.UserId}"); - } - else - { - Console.WriteLine("User is not authenticated."); - } if (identity.IsAuthenticated && identity.Id != command.UserId) { throw new UnauthorizedCommentAccessException(command.ContextId, identity.Id); @@ -153,7 +144,6 @@ public async Task HandleAsync(CreateComment command, CancellationToken cancellat } } - await _messageBroker.PublishAsync(new CommentCreated( comment.Id, comment.ContextId, @@ -164,7 +154,9 @@ await _messageBroker.PublishAsync(new CommentCreated( comment.CreatedAt, comment.LastUpdatedAt, comment.RepliesCount, - comment.IsDeleted + comment.IsDeleted, + user.FirstName + " " + user.LastName, + user.ProfileImageUrl )); } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs index c4fc5e79a..7ad444a77 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteCommentHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Application.Services; using MiniSpace.Services.Comments.Core.Entities; @@ -94,7 +94,7 @@ public async Task HandleAsync(DeleteComment command, CancellationToken cancellat break; } - await _messageBroker.PublishAsync(new CommentDeleted(command.CommentId)); + // await _messageBroker.PublishAsync(new CommentDeleted(command.CommentId)); } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs index 75d6bd4df..eceb4e460 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/DeleteLikeHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Comments.Application.Events; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Application.Services; @@ -42,7 +42,7 @@ public async Task HandleAsync(DeleteLike command, CancellationToken cancellation comment.UnLike(identity.Id); await _commentRepository.UpdateAsync(comment); - await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId)); + // await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId)); } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs index 5f986c966..381566b6f 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/Handlers/UpdateCommentHandler.cs @@ -1,12 +1,13 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Comments.Application.Events; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Application.Services; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; +using MiniSpace.Services.Comments.Application.Services.Clients; namespace MiniSpace.Services.Comments.Application.Commands.Handlers { @@ -19,6 +20,7 @@ public class UpdateCommentHandler : ICommandHandler private readonly IAppContext _appContext; private readonly IMessageBroker _messageBroker; private readonly IDateTimeProvider _dateTimeProvider; + private readonly IStudentsServiceClient _userServiceClient; public UpdateCommentHandler( IOrganizationEventsCommentRepository organizationEventsCommentRepository, @@ -27,7 +29,8 @@ public UpdateCommentHandler( IUserPostsCommentRepository userPostsCommentRepository, IAppContext appContext, IMessageBroker messageBroker, - IDateTimeProvider dateTimeProvider) + IDateTimeProvider dateTimeProvider, + IStudentsServiceClient userServiceClient) { _organizationEventsCommentRepository = organizationEventsCommentRepository; _organizationPostsCommentRepository = organizationPostsCommentRepository; @@ -36,6 +39,7 @@ public UpdateCommentHandler( _appContext = appContext; _messageBroker = messageBroker; _dateTimeProvider = dateTimeProvider; + _userServiceClient = userServiceClient; } public async Task HandleAsync(UpdateComment command, CancellationToken cancellationToken = default) @@ -64,12 +68,13 @@ public async Task HandleAsync(UpdateComment command, CancellationToken cancellat throw new InvalidCommentContextEnumException(command.CommentContext); } - if (comment is null) + if (comment == null) { throw new CommentNotFoundException(command.CommentId); } var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != comment.UserId) { throw new UnauthorizedCommentAccessException(command.CommentId, identity.Id); @@ -82,21 +87,32 @@ public async Task HandleAsync(UpdateComment command, CancellationToken cancellat case nameof(CommentContext.OrganizationEvent): await _organizationEventsCommentRepository.UpdateAsync(comment); break; - case nameof(CommentContext.OrganizationPost): await _organizationPostsCommentRepository.UpdateAsync(comment); break; - case nameof(CommentContext.UserEvent): await _userEventsCommentRepository.UpdateAsync(comment); break; - case nameof(CommentContext.UserPost): await _userPostsCommentRepository.UpdateAsync(comment); break; } - await _messageBroker.PublishAsync(new CommentUpdated(command.CommentId)); + var user = await _userServiceClient.GetAsync(identity.Id); + if (user == null) + { + throw new UserNotFoundException(identity.Id); + } + + await _messageBroker.PublishAsync(new CommentUpdated( + commentId: command.CommentId, + userId: identity.Id, + commentContext: command.CommentContext, + updatedAt: _dateTimeProvider.Now, + commentContent: command.TextContent, + userName: $"{user.FirstName} {user.LastName}", + profileImageUrl: user.ProfileImageUrl + )); } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs index e113a499a..94a17eab9 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Commands/UpdateComment.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Comments.Application.Commands { diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs index 5fbef70aa..c67fe505a 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Comments.Application.Events @@ -14,11 +14,14 @@ public class CommentCreated : IEvent public DateTime CreatedAt { get; } public DateTime LastUpdatedAt { get; } public int RepliesCount { get; } - public bool IsDeleted { get; } + public bool IsDeleted { get; } + public string UserName { get; } + public string ProfileImageUrl { get; } public CommentCreated(Guid commentId, Guid contextId, string commentContext, Guid userId, Guid parentId, string textContent, DateTime createdAt, - DateTime lastUpdatedAt, int repliesCount, bool isDeleted) + DateTime lastUpdatedAt, int repliesCount, bool isDeleted, + string userName, string profileImageUrl) { CommentId = commentId; ContextId = contextId; @@ -30,6 +33,8 @@ public CommentCreated(Guid commentId, Guid contextId, string commentContext, Gui LastUpdatedAt = lastUpdatedAt; RepliesCount = repliesCount; IsDeleted = isDeleted; + UserName = userName; + ProfileImageUrl = profileImageUrl; } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentDeleted.cs index d30ad3f1b..39a98bd13 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentDeleted.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Comments.Application.Events diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentUpdated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentUpdated.cs index 360c41cc1..584ab57f8 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentUpdated.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/CommentUpdated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Comments.Application.Events @@ -6,10 +6,22 @@ namespace MiniSpace.Services.Comments.Application.Events public class CommentUpdated : IEvent { public Guid CommentId { get; } + public Guid UserId { get; } + public string CommentContext { get; } + public DateTime UpdatedAt { get; } + public string CommentContent { get; } + public string UserName { get; } + public string ProfileImageUrl { get; } - public CommentUpdated(Guid commentId) + public CommentUpdated(Guid commentId, Guid userId, string commentContext, DateTime updatedAt, string commentContent, string userName, string profileImageUrl) { CommentId = commentId; + UserId = userId; + CommentContext = commentContext; + UpdatedAt = updatedAt; + CommentContent = commentContent; + UserName = userName; + ProfileImageUrl = profileImageUrl; } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/EventDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/EventDeleted.cs index d902fb896..eeb50ac31 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/EventDeleted.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/EventDeleted.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Comments.Application.Events.External diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/EventDeletedHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/EventDeletedHandler.cs index 9392c392b..a72bed14f 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/EventDeletedHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/EventDeletedHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Core.Repositories; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/PostDeletedHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/PostDeletedHandler.cs index 7c69cbf7a..17e27fad0 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/PostDeletedHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/Handlers/PostDeletedHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Core.Repositories; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/PostDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/PostDeleted.cs index 0163b3514..e83e9188f 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/PostDeleted.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/External/PostDeleted.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Comments.Application.Events.External diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeAdded.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeAdded.cs new file mode 100644 index 000000000..841dfe564 --- /dev/null +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeAdded.cs @@ -0,0 +1,25 @@ +using Paralax.CQRS.Events; +using System; + +namespace MiniSpace.Services.Comments.Application.Events +{ + public class LikeAdded : IEvent + { + public Guid CommentId { get; } + public Guid UserId { get; } + public string CommentContext { get; } + public DateTime LikedAt { get; } + public string UserName { get; } + public string ProfileImageUrl { get; } + + public LikeAdded(Guid commentId, Guid userId, string commentContext, DateTime likedAt, string userName, string profileImageUrl) + { + CommentId = commentId; + UserId = userId; + CommentContext = commentContext; + LikedAt = likedAt; + UserName = userName; + ProfileImageUrl = profileImageUrl; + } + } +} diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeDeleted.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeDeleted.cs index d33a5ef6e..9892b1ab3 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeDeleted.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeUpdated.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeUpdated.cs index 076cee7e2..fd83fb830 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeUpdated.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/LikeUpdated.cs @@ -1,18 +1,25 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Diagnostics.CodeAnalysis; - namespace MiniSpace.Services.Comments.Application.Events { [ExcludeFromCodeCoverage] public class LikeUpdated : IEvent { public Guid CommentId { get; } + public Guid UserId { get; } + public string CommentContext { get; } + public DateTime UpdatedAt { get; } + public string CommentContent { get; } - public LikeUpdated(Guid commentId) + public LikeUpdated(Guid commentId, Guid userId, string commentContext, DateTime updatedAt, string commentContent) { CommentId = commentId; + UserId = userId; + CommentContext = commentContext; + UpdatedAt = updatedAt; + CommentContent = commentContent; } } } diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/CreateCommentRejected.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/CreateCommentRejected.cs index a9eff9995..19d79ad58 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/CreateCommentRejected.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/CreateCommentRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/DeleteCommentRejected.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/DeleteCommentRejected.cs index 8e532ba34..752330050 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/DeleteCommentRejected.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/DeleteCommentRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/UpdateCommentRejected.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/UpdateCommentRejected.cs index 12cfb836a..6be415308 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/UpdateCommentRejected.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Events/Rejected/UpdateCommentRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Extensions.cs index 081f3bb64..6868d0c13 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Extensions.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Extensions.cs @@ -1,6 +1,6 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Comments.Application @@ -8,7 +8,7 @@ namespace MiniSpace.Services.Comments.Application [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/MiniSpace.Services.Comments.Application.csproj b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/MiniSpace.Services.Comments.Application.csproj index 6b085f9b4..df3a2ff1b 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/MiniSpace.Services.Comments.Application.csproj +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/MiniSpace.Services.Comments.Application.csproj @@ -6,11 +6,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/GetComment.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/GetComment.cs index 80b9597d0..813c44b6e 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/GetComment.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/GetComment.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Comments.Application.Dto; namespace MiniSpace.Services.Comments.Application.Queries diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/SearchComments.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/SearchComments.cs index 6f49f714c..7b3c87393 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/SearchComments.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Queries/SearchComments.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Comments.Application.Dto; using MiniSpace.Services.Comments.Core.Wrappers; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs index c6f499693..bea982568 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Comments.Core.Events; using System.Collections.Generic; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IMessageBroker.cs index 64c9d4e65..299958040 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContextFactory.cs index 55111472e..9c5c3b463 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Comments.Application; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 7bfb24faf..3cfa26cc5 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Comments.Infrastructure.Decorators diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index f70a293a9..4806af72f 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Comments.Infrastructure.Decorators diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 7f5f521f5..01b4c7aeb 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Application.Events.Rejected; using MiniSpace.Services.Comments.Application.Exceptions; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 8c7beb2aa..cf677bf9b 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,8 +1,8 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Comments.Application.Exceptions; using MiniSpace.Services.Comments.Core.Exceptions; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs index 38ac1c4c9..f250d38fd 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -47,7 +47,7 @@ namespace MiniSpace.Services.Comments.Infrastructure [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -95,7 +95,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/Extensions.cs index cab5f9fe9..79219e676 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/Extensions.cs @@ -1,5 +1,5 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; +using Paralax.CQRS.Logging; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Comments.Application.Commands; using System.Diagnostics.CodeAnalysis; @@ -9,7 +9,7 @@ namespace MiniSpace.Services.Comments.Infrastructure.Logging [ExcludeFromCodeCoverage] internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(UpdateComment).Assembly; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 293a4ffa4..52370a748 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,4 +1,4 @@ -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Application.Events; using MiniSpace.Services.Comments.Application.Events.External; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.csproj b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.csproj index 11a87d0b2..64ae9be16 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.csproj +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/MiniSpace.Services.Comments.Infrastructure.csproj @@ -8,29 +8,34 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs index 40f274ea6..d3c2c27b0 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/CommentDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Comments.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentDocument.cs index 6b2a280f0..72a21fbe6 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentDocument.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationEventCommentDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentDocument.cs index 0709cede2..de3ac2697 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentDocument.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/OrganizationPostCommentDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/ReplyDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/ReplyDocument.cs index 885d77fe6..09b61e9c7 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/ReplyDocument.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/ReplyDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Comments.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentDocument.cs index 0f650ae5c..a29cf9fc0 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentDocument.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserEventCommentDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentDocument.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentDocument.cs index 710e7b990..545993967 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentDocument.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Documents/UserPostCommentDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/GetCommentHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/GetCommentHandler.cs index 1be433d78..e14ff30b5 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/GetCommentHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/GetCommentHandler.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Comments.Application.Dto; using MiniSpace.Services.Comments.Application.Queries; using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/SearchCommentsHandler.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/SearchCommentsHandler.cs index 388ff24a3..8d51d118a 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/SearchCommentsHandler.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Queries/Handlers/SearchCommentsHandler.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Comments.Application.Dto; using MiniSpace.Services.Comments.Application.Queries; using MiniSpace.Services.Comments.Core.Entities; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs index c91c936ad..2655b1d20 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; using MiniSpace.Services.Comments.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationEventsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationEventsCommentRepository.cs index e662efd4e..fc58b9c8f 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationEventsCommentRepository.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationEventsCommentRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Comments.Application.Wrappers; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationPostsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationPostsCommentRepository.cs index 268ff6e1d..c3ef61807 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationPostsCommentRepository.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/OrganizationPostsCommentRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Comments.Application.Wrappers; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserEventsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserEventsCommentRepository.cs index 53591739a..5698bf9bb 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserEventsCommentRepository.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserEventsCommentRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Comments.Application.Wrappers; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserPostsCommentRepository.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserPostsCommentRepository.cs index 3edf48cba..faa52d0db 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserPostsCommentRepository.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Mongo/Repositories/UserPostsCommentRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Comments.Application.Wrappers; using MiniSpace.Services.Comments.Core.Entities; using MiniSpace.Services.Comments.Core.Repositories; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/Clients/StudentsServiceClient.cs index 81e3883d4..1256b879b 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Comments.Application.Dto; using MiniSpace.Services.Comments.Application.Services.Clients; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs index 3c5a3d788..83ca15995 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Comments.Application.Services; using MiniSpace.Services.Comments.Core; using MiniSpace.Services.Comments.Core.Events; diff --git a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/MessageBroker.cs index 935d792e0..e6b32fc99 100644 --- a/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Comments/src/MiniSpace.Services.Comments.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Application.UnitTests/Commands/Handlers/CreateCommentHandlerTest.cs b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Application.UnitTests/Commands/Handlers/CreateCommentHandlerTest.cs index d24036a62..59cbeb787 100644 --- a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Application.UnitTests/Commands/Handlers/CreateCommentHandlerTest.cs +++ b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Application.UnitTests/Commands/Handlers/CreateCommentHandlerTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Comments.Application.Commands.Handlers; using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/AggregatedIdTest.cs b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/AggregatedIdTest.cs index 7781912e5..cbed9b280 100644 --- a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/AggregatedIdTest.cs +++ b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/AggregatedIdTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Comments.Application.Commands.Handlers; using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/CommentTest.cs b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/CommentTest.cs index 37e364fa6..ca144ad3b 100644 --- a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/CommentTest.cs +++ b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Core.UnitTests/Entities/CommentTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Comments.Application.Commands.Handlers; using MiniSpace.Services.Comments.Application.Commands; using MiniSpace.Services.Comments.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Infrastructure.UnitTests/Services/MessageBrokerTest.cs b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Infrastructure.UnitTests/Services/MessageBrokerTest.cs index 0976929bc..8c79a1e74 100644 --- a/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Infrastructure.UnitTests/Services/MessageBrokerTest.cs +++ b/MiniSpace.Services.Comments/tests/MiniSpace.Services.Comments.Infrastructure.UnitTests/Services/MessageBrokerTest.cs @@ -1,12 +1,12 @@ using Xunit; using Moq; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Collections.Generic; using System.Threading.Tasks; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.csproj b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.csproj index 7219ce31d..3b19f90fb 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.csproj +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/MiniSpace.Services.Communication.Api.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Program.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Program.cs index 601b5b58d..449c6d4ef 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Program.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Api/Program.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.Builder; @@ -20,6 +20,8 @@ using MiniSpace.Services.Communication.Application.Hubs; using MiniSpace.Services.Communication.Core.Wrappers; using System; +using Paralax.Types; + namespace MiniSpace.Services.Communication.Api { @@ -29,7 +31,7 @@ public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => { - services.AddConvey() + services.AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure(); diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/AddUserToChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/AddUserToChat.cs index b9c8b36bf..20410c992 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/AddUserToChat.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/AddUserToChat.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Communication.Application.Commands diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/CreateChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/CreateChat.cs index 8a81dfd02..4ea5f45b1 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/CreateChat.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/CreateChat.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteChat.cs index f1cf27b57..f47bb6f99 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteChat.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteChat.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.AspNetCore.Mvc; using System; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteMessage.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteMessage.cs index b6fc99d17..7353ff338 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteMessage.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/DeleteMessage.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.AspNetCore.Mvc; using System; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/AddUserToChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/AddUserToChatHandler.cs index 9a0bbd1be..f8e4226ba 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/AddUserToChatHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/AddUserToChatHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Communication.Application.Commands; using MiniSpace.Services.Communication.Application.Events; using MiniSpace.Services.Communication.Application.Services; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/CreateChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/CreateChatHandler.cs index 121c5316a..f6e0537a6 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/CreateChatHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/CreateChatHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.Extensions.Logging; using MiniSpace.Services.Communication.Application.Commands; using MiniSpace.Services.Communication.Application.Services; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteChatHandler.cs index 3622fc5f5..e6410a945 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteChatHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteChatHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Communication.Application.Commands; using MiniSpace.Services.Communication.Application.Events; using MiniSpace.Services.Communication.Application.Services; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteMessageHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteMessageHandler.cs index daaf6af7b..6c539f74d 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteMessageHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/DeleteMessageHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Communication.Application.Commands; using MiniSpace.Services.Communication.Application.Services; using MiniSpace.Services.Communication.Core.Repositories; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/RemoveUserFromChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/RemoveUserFromChatHandler.cs index 5a7291c36..181298614 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/RemoveUserFromChatHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/RemoveUserFromChatHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Communication.Application.Commands; using MiniSpace.Services.Communication.Application.Events; using MiniSpace.Services.Communication.Application.Services; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/SendMessageHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/SendMessageHandler.cs index c273375f6..e3fd1c19f 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/SendMessageHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/SendMessageHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.SignalR; using MiniSpace.Services.Communication.Application.Commands; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/UpdateMessageStatusHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/UpdateMessageStatusHandler.cs index c18402d17..95dff27ba 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/UpdateMessageStatusHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/Handlers/UpdateMessageStatusHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.SignalR; using MiniSpace.Services.Communication.Application.Commands; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/RemoveUserFromChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/RemoveUserFromChat.cs index 53de89eaa..86c4a9d31 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/RemoveUserFromChat.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/RemoveUserFromChat.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Communication.Application.Commands diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/SendMessage.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/SendMessage.cs index a5df08bbb..5c69762f5 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/SendMessage.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/SendMessage.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Communication.Application.Commands diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/UpdateMessageStatus.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/UpdateMessageStatus.cs index cf809704e..d1341b4fe 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/UpdateMessageStatus.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Commands/UpdateMessageStatus.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Communication.Application.Commands diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatCreated.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatCreated.cs index 736768c16..127fc2611 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatCreated.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatDeleted.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatDeleted.cs index 6c06f258a..07a0b2ac0 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatDeleted.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/ChatDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageSent.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageSent.cs index 8d0d17727..a4b79de83 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageSent.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageSent.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageStatusUpdated.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageStatusUpdated.cs index 359ed6415..5f0f114bc 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageStatusUpdated.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/MessageStatusUpdated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatCreationRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatCreationRejected.cs index 063316929..ae6313314 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatCreationRejected.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatCreationRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events.Rejected diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatDeletionRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatDeletionRejected.cs index 7ea9a517a..d5ee51dc3 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatDeletionRejected.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatDeletionRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events.Rejected diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatProcessRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatProcessRejected.cs index 51363312c..3b326a105 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatProcessRejected.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/ChatProcessRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events.Rejected diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageProcessRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageProcessRejected.cs index 334a1f1f5..f4c858110 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageProcessRejected.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageProcessRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events.Rejected diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageSendRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageSendRejected.cs index 61629d6a5..7be66eaa0 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageSendRejected.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/MessageSendRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events.Rejected diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/UserAdditionToChatRejected.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/UserAdditionToChatRejected.cs index 5589a4c38..b29b1d71b 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/UserAdditionToChatRejected.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/Rejected/UserAdditionToChatRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events.Rejected diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/UserAddedToChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/UserAddedToChat.cs index 84c8c1de1..1432bbf0a 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/UserAddedToChat.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Events/UserAddedToChat.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Communication.Application.Events diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Extensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Extensions.cs index d03c02a20..8af94e719 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Extensions.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Extensions.cs @@ -1,12 +1,12 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Communication.Application { public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.csproj b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.csproj index 021af6a32..12b7ee187 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.csproj +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/MiniSpace.Services.Communication.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetChatById.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetChatById.cs index db0a551a5..39d1dd95d 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetChatById.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetChatById.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Communication.Application.Dto; using System; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetMessagesForChat.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetMessagesForChat.cs index fb7d10508..28d45d20c 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetMessagesForChat.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetMessagesForChat.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Communication.Application.Dto; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetUserChats.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetUserChats.cs index 1728621e0..2253576b8 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetUserChats.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Queries/GetUserChats.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Communication.Application.Dto; using System; using MiniSpace.Services.Communication.Core.Wrappers; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IEventMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IEventMapper.cs index 0cde987ee..a3c63fa9d 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Communication.Core.Events; namespace MiniSpace.Services.Communication.Application.Services diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IMessageBroker.cs index 405bad2e8..d4e9d1f26 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Communication.Application.Services { diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContextFactory.cs index 8f300c3d8..a5e851fcb 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Communication.Application; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 849f12031..9e52bead9 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Communication.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 3e1e88706..1dce1cbbc 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Communication.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index dd910203c..d43836bad 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Communication.Application.Commands; using MiniSpace.Services.Communication.Application.Events.Rejected; using MiniSpace.Services.Communication.Application.Exceptions; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 6338b7896..1a6156650 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Communication.Application.Exceptions; using MiniSpace.Services.Communication.Core.Exceptions; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Extensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Extensions.cs index a0779ab56..ddf28ddf9 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -45,7 +45,7 @@ namespace MiniSpace.Services.Communication.Infrastructure { public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -92,7 +92,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() @@ -108,7 +108,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app return app; } - public static IConveyBuilder AddSignalRInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddSignalRInfrastructure(this IParalaxBuilder builder) { builder.Services.AddCors(options => { diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/Extensions.cs index ef9726c4f..ff26e8edf 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/Extensions.cs @@ -1,5 +1,5 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; +using Paralax.CQRS.Logging; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Communication.Application.Commands; using System.Reflection; @@ -8,7 +8,7 @@ namespace MiniSpace.Services.Communication.Infrastructure.Logging { internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(UpdateMessageStatus).Assembly; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/MessageToLogTemplateMapper.cs index bfe40da1b..1587f32db 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,4 +1,4 @@ -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using Microsoft.Extensions.Logging; using MiniSpace.Services.Communication.Application.Commands; using System; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.csproj b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.csproj index 4992a6cf3..0dc0c9368 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.csproj +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/MiniSpace.Services.Communication.Infrastructure.csproj @@ -7,32 +7,37 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/ChatDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/ChatDocument.cs index 98bf65ee4..969e02034 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/ChatDocument.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/ChatDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/MessageDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/MessageDocument.cs index 24ecd3022..843eee5c9 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/MessageDocument.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/MessageDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using MiniSpace.Services.Communication.Core.Entities; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsDocument.cs index f65e70d73..ecc21d65c 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsDocument.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/OrganizationChatsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsDocument.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsDocument.cs index 8e39b3f25..1162c7903 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsDocument.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Documents/UserChatsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetChatByIdHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetChatByIdHandler.cs index bd7bb4376..376f6ccd7 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetChatByIdHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetChatByIdHandler.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Communication.Application.Dto; using MiniSpace.Services.Communication.Application.Queries; using MiniSpace.Services.Communication.Core.Repositories; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetMessagesForChatHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetMessagesForChatHandler.cs index 84c91211f..9f35831ea 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetMessagesForChatHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetMessagesForChatHandler.cs @@ -3,7 +3,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Communication.Application.Dto; using MiniSpace.Services.Communication.Application.Queries; using MiniSpace.Services.Communication.Core.Repositories; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetUserChatsHandler.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetUserChatsHandler.cs index aa0ede7b9..162e9aa62 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetUserChatsHandler.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Queries/Handlers/GetUserChatsHandler.cs @@ -1,6 +1,6 @@ using System.Linq; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents; using MiniSpace.Services.Communication.Application.Dto; using MiniSpace.Services.Communication.Application.Queries; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/OrganizationChatsRepository.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/OrganizationChatsRepository.cs index 909cd3a47..13286abe1 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/OrganizationChatsRepository.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/OrganizationChatsRepository.cs @@ -2,7 +2,7 @@ using MiniSpace.Services.Communication.Core.Repositories; using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents; using MongoDB.Driver; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using System; using System.Threading.Tasks; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/UserChatsRepository.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/UserChatsRepository.cs index 1db100707..2928fd694 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/UserChatsRepository.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Mongo/Repositories/UserChatsRepository.cs @@ -2,7 +2,7 @@ using MiniSpace.Services.Communication.Core.Repositories; using MiniSpace.Services.Communication.Infrastructure.Mongo.Documents; using MongoDB.Driver; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using System; using System.Linq; using System.Threading.Tasks; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/Clients/StudentsServiceClient.cs index 2c20e4f2b..2455ff043 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Communication.Application.Dto; using MiniSpace.Services.Communication.Application.Queries; using MiniSpace.Services.Communication.Application.Services.Clients; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/EventMapper.cs index cfbb8a5b6..3e2b7e44d 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Communication.Application.Events; using MiniSpace.Services.Communication.Application.Services; using MiniSpace.Services.Communication.Core.Events; diff --git a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/MessageBroker.cs index 4bb6cbdd4..332c487e9 100644 --- a/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Communication/src/MiniSpace.Services.Communication.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/MiniSpace.Services.Email.Api.csproj b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/MiniSpace.Services.Email.Api.csproj index e26cebf01..02faada81 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/MiniSpace.Services.Email.Api.csproj +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/MiniSpace.Services.Email.Api.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/Program.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/Program.cs index 7793964ee..e66213fcc 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/Program.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Api/Program.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -14,6 +14,9 @@ using MiniSpace.Services.Email.Application.Dto; using MiniSpace.Services.Email.Application.Queries; using MiniSpace.Services.Email.Infrastructure; +using Paralax.Types; +using Paralax.Core; + namespace MiniSpace.Services.Email.Api { @@ -22,7 +25,7 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/CreateEmailNotification.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/CreateEmailNotification.cs index efaf71dc3..f5d3c35aa 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/CreateEmailNotification.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/CreateEmailNotification.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Email.Application.Commands diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/DeleteNotification.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/DeleteNotification.cs index bedf22383..12a9b0e11 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/DeleteNotification.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/DeleteNotification.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.AspNetCore.Mvc; namespace MiniSpace.Services.Email.Email.Commands diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/CreateEmailNotificationHandler.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/CreateEmailNotificationHandler.cs index 0a15f29b5..77fc84a94 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/CreateEmailNotificationHandler.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/CreateEmailNotificationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Email.Core.Repositories; using MiniSpace.Services.Email.Core.Entities; using System; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/DeleteEmailNotificationHandler.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/DeleteEmailNotificationHandler.cs index 279ef8094..e028b44d1 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/DeleteEmailNotificationHandler.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Commands/Handlers/DeleteEmailNotificationHandler.cs @@ -1,4 +1,4 @@ -// using Convey.CQRS.Commands; +// using Paralax.CQRS.Commands; // using MiniSpace.Services.Email.Core.Repositories; // using MiniSpace.Services.Email.Application.Exceptions; // using MiniSpace.Services.Email.Application.Services; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailCreated.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailCreated.cs index 8715e9523..a1a56e758 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailCreated.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailCreated.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Email.Application.Events { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailQueued.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailQueued.cs index ce591b4ae..1a9aa6c5c 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailQueued.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailQueued.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Email.Application.Events { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailSent.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailSent.cs index b301351a2..532f8a0af 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailSent.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/EmailSent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Email.Application.Events { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/Handlers/NotificationCreatedHandler.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/Handlers/NotificationCreatedHandler.cs index cae119ba2..14060b91e 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/Handlers/NotificationCreatedHandler.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/Handlers/NotificationCreatedHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using MiniSpace.Services.Email.Core.Entities; using MiniSpace.Services.Email.Application.Services; using MiniSpace.Services.Email.Application.Services.Clients; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/NotificationCreated.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/NotificationCreated.cs index d39ec8b86..f302b2cda 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/NotificationCreated.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/NotificationCreated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Email.Application.Events.External diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/UserStatusChanged.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/UserStatusChanged.cs index 4833032ac..35a44fec0 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/UserStatusChanged.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/External/UserStatusChanged.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using MiniSpace.Services.Email.Core.Entities; namespace MiniSpace.Services.Email.Application.Events.External diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailCreationRejected.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailCreationRejected.cs index 4e69ddf62..49408997d 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailCreationRejected.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailCreationRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Email.Application.Events.Rejected diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailQueueingRejected.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailQueueingRejected.cs index 963621269..10aaf6d7c 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailQueueingRejected.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailQueueingRejected.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Email.Application.Events.Rejected { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailSendingRejected.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailSendingRejected.cs index d696b6ea7..08bc65fa1 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailSendingRejected.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Events/Rejected/EmailSendingRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Email.Application.Events.Rejected { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Extensions.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Extensions.cs index 7000ac3d9..16c8976fe 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Extensions.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Extensions.cs @@ -1,12 +1,12 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Email.Application { public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/MiniSpace.Services.Email.Application.csproj b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/MiniSpace.Services.Email.Application.csproj index bc7210004..21f339c77 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/MiniSpace.Services.Email.Application.csproj +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/MiniSpace.Services.Email.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotification.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotification.cs index bfc777cfa..4c7a20a4c 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotification.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotification.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Email.Application.Dto; using MiniSpace.Services.Email.Core.Entities; using System; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotificationsByUser.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotificationsByUser.cs index d37c01073..96b06842e 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotificationsByUser.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/GetEmailNotificationsByUser.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Email.Application.Dto; using System; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedEmailNotificationsQuery.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedEmailNotificationsQuery.cs index 0d158d381..e30dbf693 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedEmailNotificationsQuery.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedEmailNotificationsQuery.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using System.Collections.Generic; namespace MiniSpace.Services.Email.Application.Queries diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedQuery.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedQuery.cs index be98e4a6e..70124b3d0 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedQuery.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Queries/IPagedQuery.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; namespace MiniSpace.Services.Email.Application.Queries { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IEventMapper.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IEventMapper.cs index d3990ab89..5169cd237 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Email.Core.Events; namespace MiniSpace.Services.Email.Application.Services diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IMessageBroker.cs index ccaf4d9b1..28e955f94 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Email.Application.Services { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Contexts/AppContextFactory.cs index 02c7a8c97..647fc11b9 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Email.Infrastructure; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index e6b17855c..47cc0bc34 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Email.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 0926d7062..03ed0d588 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Email.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 4862edc0d..92d297ccc 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Email.Application.Commands; using MiniSpace.Services.Email.Application.Events.Rejected; using MiniSpace.Services.Email.Application.Exceptions; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 732d82478..7c46987e7 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Email.Application.Exceptions; using MiniSpace.Services.Email.Core.Exceptions; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Extensions.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Extensions.cs index c15517e02..c7068c7ae 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -50,7 +50,7 @@ namespace MiniSpace.Services.Email.Infrastructure { public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { var smtpConfig = builder.Services.BuildServiceProvider().GetService().GetSection("smtp").Get(); builder.Services.AddSingleton(smtpConfig); @@ -105,7 +105,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/Extensions.cs index fb7601984..edece2635 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/Extensions.cs @@ -1,5 +1,5 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; +using Paralax.CQRS.Logging; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Email.Application.Commands; @@ -7,7 +7,7 @@ namespace MiniSpace.Services.Email.Infrastructure.Logging { internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(CreateEmailNotification).Assembly; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 7d10cbf8a..a232c9145 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,4 +1,4 @@ -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using MiniSpace.Services.Email.Application.Commands; using MiniSpace.Services.Email.Application.Events; using MiniSpace.Services.Email.Application.Events.External; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/MiniSpace.Services.Email.Infrastructure.csproj b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/MiniSpace.Services.Email.Infrastructure.csproj index dabeb7dd0..39d3ff5cb 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/MiniSpace.Services.Email.Infrastructure.csproj +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/MiniSpace.Services.Email.Infrastructure.csproj @@ -7,29 +7,34 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/EmailNotificationDocument.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/EmailNotificationDocument.cs index 1e2f7f2ac..902bd6727 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/EmailNotificationDocument.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/EmailNotificationDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Email.Core.Entities; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentDocument.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentDocument.cs index 7582a00cd..20692feb2 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentDocument.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentDocument.cs @@ -1,5 +1,5 @@ using System; -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Email.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentEmailsDocument.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentEmailsDocument.cs index 048db62fc..3098ec72b 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentEmailsDocument.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Documents/StudentEmailsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Queries/Handlers/GetEmailNotificationsByUserHandler.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Queries/Handlers/GetEmailNotificationsByUserHandler.cs index 388232407..8d8140fbb 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Queries/Handlers/GetEmailNotificationsByUserHandler.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Queries/Handlers/GetEmailNotificationsByUserHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Email.Application.Dto; using MiniSpace.Services.Email.Application.Queries; using MiniSpace.Services.Email.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentEmailsMongoRepository.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentEmailsMongoRepository.cs index 595aada30..544c242d9 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentEmailsMongoRepository.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentEmailsMongoRepository.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Email.Core.Entities; using MiniSpace.Services.Email.Core.Repositories; using MiniSpace.Services.Email.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs index aa3fca59a..ecea1c253 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs @@ -1,4 +1,4 @@ -// using Convey.Persistence.MongoDB; +// using Paralax.Persistence.MongoDB; // using MiniSpace.Services.Email.Core.Entities; // using MiniSpace.Services.Email.Core.Repositories; // using MiniSpace.Services.Email.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/Clients/StudentsServiceClient.cs index fba4181dd..865342604 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Email.Application.Dto; using MiniSpace.Services.Email.Application.Services.Clients; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/EventMapper.cs index c8a8d2afc..fc85a6b87 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/EventMapper.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Email.Core.Events; using MiniSpace.Services.Email.Application.Events.External; using MiniSpace.Services.Email.Application.Events; diff --git a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/MessageBroker.cs index e1ca4d636..acbb1f577 100644 --- a/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Email/src/MiniSpace.Services.Email.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/MiniSpace.Services.Events.Api.csproj b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/MiniSpace.Services.Events.Api.csproj index f7bb879b4..d6379f0ea 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/MiniSpace.Services.Events.Api.csproj +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/MiniSpace.Services.Events.Api.csproj @@ -7,14 +7,15 @@ - - - - - + + + + + - + + diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs index 42c9125f5..11b70a8d0 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Api/Program.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Secrets.Vault; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Secrets.Vault; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -23,6 +23,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Logging; using System.Text.Json; +using Paralax.Core; + namespace MiniSpace.Services.Identity.Api { @@ -31,7 +33,7 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEventParticipant.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEventParticipant.cs index 596a3c2ea..1bb4391c6 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEventParticipant.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/AddEventParticipant.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Entities; @@ -10,6 +10,5 @@ public class AddEventParticipant: ICommand { public Guid EventId { get; set; } public Guid StudentId { get; set; } - public string StudentName { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelInterestInEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelInterestInEvent.cs index c88c1157e..90fc08725 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelInterestInEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelInterestInEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelRateEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelRateEvent.cs index 8aeeaf5f4..0ffecf45a 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelRateEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelRateEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelSignUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelSignUpToEvent.cs index 4ca8d5a6f..fe4f7c007 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelSignUpToEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CancelSignUpToEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CreateEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CreateEvent.cs index d5d6d69fd..a60e36fc1 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CreateEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/CreateEvent.cs @@ -1,7 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Entities; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/DeleteEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/DeleteEvent.cs index 3deb905b8..44e1554b2 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/DeleteEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/DeleteEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventParticipantHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventParticipantHandler.cs index 585c012a1..1148de92e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventParticipantHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/AddEventParticipantHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; @@ -54,7 +54,7 @@ public async Task HandleAsync(AddEventParticipant command, CancellationToken can @event.AddParticipant(new Participant(command.StudentId)); await _eventRepository.UpdateAsync(@event); await _messageBroker.PublishAsync(new EventParticipantAdded(@event.Id, - command.StudentId, command.StudentName)); + command.StudentId)); } } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelInterestInEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelInterestInEventHandler.cs index e92c5834b..e857c5524 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelInterestInEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelInterestInEventHandler.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelRateEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelRateEventHandler.cs index a3daeacb0..fe6c9a3e5 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelRateEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelRateEventHandler.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Core.Repositories; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelSignUpToEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelSignUpToEventHandler.cs index a73533d97..c73b74e08 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelSignUpToEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CancelSignUpToEventHandler.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs index 86510e4a9..8b84e82c4 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/CreateEventHandler.cs @@ -3,7 +3,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; @@ -37,157 +37,140 @@ public CreateEventHandler(IEventRepository eventRepository, IMessageBroker messa public async Task HandleAsync(CreateEvent command, CancellationToken cancellationToken) { - try - { - var options = new JsonSerializerOptions { WriteIndented = true }; - var commandJson = JsonSerializer.Serialize(command, options); - Console.WriteLine("Received CreateEvent command: "); - Console.WriteLine(commandJson); + var options = new JsonSerializerOptions { WriteIndented = true }; + var commandJson = JsonSerializer.Serialize(command, options); + var identity = _appContext.Identity; - var identity = _appContext.Identity; + // Validate Event ID + if (command.EventId == Guid.Empty || await _eventRepository.ExistsAsync(command.EventId)) + { + throw new InvalidEventIdException(command.EventId); + } - // Validate Event ID - if (command.EventId == Guid.Empty || await _eventRepository.ExistsAsync(command.EventId)) - { - throw new InvalidEventIdException(command.EventId); - } + // Validate Organizer Type + if (!Enum.TryParse(command.OrganizerType, true, out var organizerType)) + { + throw new ArgumentException($"Invalid OrganizerType value: {command.OrganizerType}"); + } - // Validate Organizer Type - if (!Enum.TryParse(command.OrganizerType, true, out var organizerType)) - { - throw new ArgumentException($"Invalid OrganizerType value: {command.OrganizerType}"); - } + // Validate Visibility + if (!Enum.TryParse(command.Visibility, true, out var visibility)) + { + throw new ArgumentException($"Invalid Visibility value: {command.Visibility}"); + } - // Validate Visibility - if (!Enum.TryParse(command.Visibility, true, out var visibility)) + PaymentMethod? paymentMethod = null; + if (command.Settings != null && command.Settings.RequiresPayment && !string.IsNullOrWhiteSpace(command.Settings.PaymentMethod)) + { + if (!Enum.TryParse(command.Settings.PaymentMethod, true, out var parsedPaymentMethod)) { - throw new ArgumentException($"Invalid Visibility value: {command.Visibility}"); + throw new ArgumentException($"Invalid PaymentMethod value: {command.Settings.PaymentMethod}"); } + paymentMethod = parsedPaymentMethod; + } - PaymentMethod? paymentMethod = null; - if (command.Settings != null && command.Settings.RequiresPayment && !string.IsNullOrWhiteSpace(command.Settings.PaymentMethod)) - { - if (!Enum.TryParse(command.Settings.PaymentMethod, true, out var parsedPaymentMethod)) - { - throw new ArgumentException($"Invalid PaymentMethod value: {command.Settings.PaymentMethod}"); - } - paymentMethod = parsedPaymentMethod; - } + _eventValidator.ValidateName(command.Name); + _eventValidator.ValidateDescription(command.Description); + var startDate = _eventValidator.ParseDate(command.StartDate, "event_start_date"); + var endDate = _eventValidator.ParseDate(command.EndDate, "event_end_date"); + var now = _dateTimeProvider.Now; + _eventValidator.ValidateDates(now, startDate, "now", "event_start_date"); + _eventValidator.ValidateDates(startDate, endDate, "event_start_date", "event_end_date"); + + // Create Address object + var address = new Address(command.BuildingName, command.Street, command.BuildingNumber, command.ApartmentNumber, command.City, command.ZipCode, command.Country); + + // Validate Capacity and Fee + _eventValidator.ValidateCapacity(command.Capacity); + _eventValidator.ValidateFee(command.Fee); + + // Parse and Validate Category + var category = _eventValidator.ParseCategory(command.Category); + + // Determine Publish Date and State + var publishDate = now; + var state = State.Published; + if (!string.IsNullOrEmpty(command.PublishDate)) + { + publishDate = _eventValidator.ParseDate(command.PublishDate, "event_publish_date"); + _eventValidator.ValidateDates(now, publishDate, "now", "event_publish_date"); + _eventValidator.ValidateDates(publishDate, startDate, "event_publish_date", "event_start_date"); + state = State.ToBePublished; + } - _eventValidator.ValidateName(command.Name); - _eventValidator.ValidateDescription(command.Description); - var startDate = _eventValidator.ParseDate(command.StartDate, "event_start_date"); - var endDate = _eventValidator.ParseDate(command.EndDate, "event_end_date"); - var now = _dateTimeProvider.Now; - _eventValidator.ValidateDates(now, startDate, "now", "event_start_date"); - _eventValidator.ValidateDates(startDate, endDate, "event_start_date", "event_end_date"); - - // Create Address object - var address = new Address(command.BuildingName, command.Street, command.BuildingNumber, command.ApartmentNumber, command.City, command.ZipCode, command.Country); - - // Validate Capacity and Fee - _eventValidator.ValidateCapacity(command.Capacity); - _eventValidator.ValidateFee(command.Fee); - - // Parse and Validate Category - var category = _eventValidator.ParseCategory(command.Category); - - // Determine Publish Date and State - var publishDate = now; - var state = State.Published; - if (!string.IsNullOrEmpty(command.PublishDate)) + // Determine Organizer + Organizer organizer; + if (organizerType == OrganizerType.Organization) + { + if (command.OrganizationId == null) { - publishDate = _eventValidator.ParseDate(command.PublishDate, "event_publish_date"); - _eventValidator.ValidateDates(now, publishDate, "now", "event_publish_date"); - _eventValidator.ValidateDates(publishDate, startDate, "event_publish_date", "event_start_date"); - state = State.ToBePublished; + throw new ArgumentNullException(nameof(command.OrganizationId), "OrganizationId cannot be null for Organization-type events."); } - // Determine Organizer - Organizer organizer; - if (organizerType == OrganizerType.Organization) - { - if (command.OrganizationId == null) - { - throw new ArgumentNullException(nameof(command.OrganizationId), "OrganizationId cannot be null for Organization-type events."); - } - - var organization = await _organizationsServiceClient.GetAsync(command.OrganizationId.Value); - if (organization == null) - { - throw new OrganizationNotFoundException(command.OrganizationId.Value); - } - - // Store both organization ID and the user ID creating the event - organizer = new Organizer(command.OrganizationId.Value, OrganizerType.Organization, userId: command.OrganizerId, organizationId: command.OrganizationId.Value); - } - else + var organization = await _organizationsServiceClient.GetAsync(command.OrganizationId.Value); + if (organization == null) { - organizer = new Organizer(command.OrganizerId, OrganizerType.User, userId: command.OrganizerId); + throw new OrganizationNotFoundException(command.OrganizationId.Value); } - var settings = command.Settings != null - ? new EventSettings - { - RequiresApproval = command.Settings.RequiresApproval, - IsOnlineEvent = command.Settings.IsOnlineEvent, - IsPrivate = command.Settings.IsPrivate, - RequiresRSVP = command.Settings.RequiresRSVP, - AllowsGuests = command.Settings.AllowsGuests, - ShowAttendeesPublicly = command.Settings.ShowAttendeesPublicly, - SendReminders = command.Settings.SendReminders, - ReminderDaysBefore = command.Settings.ReminderDaysBefore, - EnableChat = command.Settings.EnableChat, - AllowComments = command.Settings.AllowComments, - RequiresPayment = command.Settings.RequiresPayment, - PaymentMethod = paymentMethod ?? PaymentMethod.Offline, - PaymentReceiverDetails = command.Settings.PaymentReceiverDetails, - PaymentGateway = command.Settings.PaymentGateway, - IssueTickets = command.Settings.IssueTickets, - MaxTicketsPerPerson = command.Settings.MaxTicketsPerPerson, - TicketPrice = command.Settings.TicketPrice, - RecordEvent = command.Settings.RecordEvent, - CustomTermsAndConditions = command.Settings.CustomTermsAndConditions, - CustomFields = command.Settings.CustomFields - } - : null; - - var @event = Event.Create( - command.EventId, - command.Name, - command.Description, - organizer, - startDate, - endDate, - address, - command.MediaFilesUrl.ToList(), - command.BannerUrl, - command.Capacity, - command.Fee, - category, - state, - publishDate, - now, - visibility, - settings); - - await _eventRepository.AddAsync(@event); - await _messageBroker.PublishAsync(new EventCreated( - @event.Id, - @event.Organizer.OrganizerType, - @event.Organizer.Id, - @event.MediaFiles)); - } - catch (ArgumentException argEx) - { - Console.WriteLine($"Validation error: {argEx.Message}"); - throw; + organizer = new Organizer(command.OrganizationId.Value, OrganizerType.Organization, userId: command.OrganizerId, organizationId: command.OrganizationId.Value); } - catch (Exception ex) + else { - Console.WriteLine($"Unhandled exception: {ex.Message}"); - throw; + organizer = new Organizer(command.OrganizerId, OrganizerType.User, userId: command.OrganizerId); } + + var settings = command.Settings != null + ? new EventSettings + { + RequiresApproval = command.Settings.RequiresApproval, + IsOnlineEvent = command.Settings.IsOnlineEvent, + IsPrivate = command.Settings.IsPrivate, + RequiresRSVP = command.Settings.RequiresRSVP, + AllowsGuests = command.Settings.AllowsGuests, + ShowAttendeesPublicly = command.Settings.ShowAttendeesPublicly, + SendReminders = command.Settings.SendReminders, + ReminderDaysBefore = command.Settings.ReminderDaysBefore, + EnableChat = command.Settings.EnableChat, + AllowComments = command.Settings.AllowComments, + RequiresPayment = command.Settings.RequiresPayment, + PaymentMethod = paymentMethod ?? PaymentMethod.Offline, + PaymentReceiverDetails = command.Settings.PaymentReceiverDetails, + PaymentGateway = command.Settings.PaymentGateway, + IssueTickets = command.Settings.IssueTickets, + MaxTicketsPerPerson = command.Settings.MaxTicketsPerPerson, + TicketPrice = command.Settings.TicketPrice, + RecordEvent = command.Settings.RecordEvent, + CustomTermsAndConditions = command.Settings.CustomTermsAndConditions, + CustomFields = command.Settings.CustomFields + } + : null; + + var @event = Event.Create( + command.EventId, + command.Name, + command.Description, + organizer, + startDate, + endDate, + address, + command.MediaFilesUrl.ToList(), + command.BannerUrl, + command.Capacity, + command.Fee, + category, + state, + publishDate, + now, + visibility, + settings); + + await _eventRepository.AddAsync(@event); + await _messageBroker.PublishAsync(new EventCreated( + @event.Id, + @event.Organizer.OrganizerType, + @event.Organizer.Id, + @event.MediaFiles)); } } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/DeleteEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/DeleteEventHandler.cs index 61bb2e83a..1a358ccb6 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/DeleteEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/DeleteEventHandler.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; @@ -42,7 +42,18 @@ public async Task HandleAsync(DeleteEvent command, CancellationToken cancellatio } await _eventRepository.DeleteAsync(command.EventId); - await _messageBroker.PublishAsync(new EventDeleted(command.EventId)); + + var organizerId = @event.Organizer.OrganizerType == OrganizerType.User + ? @event.Organizer.UserId.GetValueOrDefault() + : @event.Organizer.OrganizationId.GetValueOrDefault(); + + await _messageBroker.PublishAsync(new EventDeleted( + command.EventId, + @event.Name, + organizerId, + @event.StartDate, + @event.EndDate + )); } } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RateEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RateEventHandler.cs index 1f4193386..c1c22e469 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RateEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RateEventHandler.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services.Clients; using MiniSpace.Services.Events.Core.Repositories; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RemoveEventParticipantHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RemoveEventParticipantHandler.cs index 5cfb309de..a32dd76ae 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RemoveEventParticipantHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/RemoveEventParticipantHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs index 92e3a2d30..1228f92ea 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ShowInterestInEventHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs index 2a039efd4..0ae2c3c76 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/SignUpToEventHandler.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventHandler.cs index 1001f7acd..021b5a98b 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventHandler.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Application.Services; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventsStateHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventsStateHandler.cs index e0139ae43..d63bb06dc 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventsStateHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/UpdateEventsStateHandler.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Services; using MiniSpace.Services.Events.Core.Entities; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ViewEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ViewEventHandler.cs index a99f9cff2..ea79c7209 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ViewEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/Handlers/ViewEventHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Core.Entities; using MiniSpace.Services.Events.Core.Repositories; using Microsoft.Extensions.Logging; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RateEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RateEvent.cs index d25b239e5..57831bfb3 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RateEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RateEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RemoveEventParticipant.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RemoveEventParticipant.cs index 679c46efb..b885fd26b 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RemoveEventParticipant.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/RemoveEventParticipant.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs index d49091735..b65091739 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchEvents.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.DTO; namespace MiniSpace.Services.Events.Application.Commands diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchOrganizerEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchOrganizerEvents.cs index e51c824fd..d0c2e1c5c 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchOrganizerEvents.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SearchOrganizerEvents.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Application.DTO; namespace MiniSpace.Services.Events.Application.Commands diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ShowInterestInEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ShowInterestInEvent.cs index ac3e05df3..510892c78 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ShowInterestInEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ShowInterestInEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SignUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SignUpToEvent.cs index 13009860d..a4a8ffc3b 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SignUpToEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/SignUpToEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEvent.cs index 460e798f9..629c9343c 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEvent.cs @@ -1,7 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Application.Commands diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEventsState.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEventsState.cs index 3fa7e5669..c62375e40 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEventsState.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/UpdateEventsState.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ViewEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ViewEvent.cs index d1a668dea..1b71a1225 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ViewEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Commands/ViewEvent.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Events.Application.Commands { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs index b06e3af26..328de7416 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/EventDto.cs @@ -60,9 +60,9 @@ public EventDto(Event @event, Guid studentId) UpdatedAt = @event.UpdatedAt; Visibility = @event.Visibility; Settings = new EventSettingsDto(@event.Settings); - IsSignedUp = @event.SignedUpParticipants.Any(x => x.StudentId == studentId); - IsInterested = @event.InterestedParticipants.Any(x => x.StudentId == studentId); - StudentRating = @event.Ratings.FirstOrDefault(x => x.StudentId == studentId)?.Value; + IsSignedUp = @event.SignedUpParticipants.Any(x => x.UserId == studentId); + IsInterested = @event.InterestedParticipants.Any(x => x.UserId == studentId); + StudentRating = @event.Ratings.FirstOrDefault(x => x.UserId == studentId)?.Value; FriendsInterestedIn = Enumerable.Empty(); FriendsSignedUp = Enumerable.Empty(); } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ParticipantDto.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ParticipantDto.cs index 52fa6a463..aa1f4febc 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ParticipantDto.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/DTO/ParticipantDto.cs @@ -6,6 +6,6 @@ namespace MiniSpace.Services.Events.Application.DTO [ExcludeFromCodeCoverage] public class ParticipantDto { - public Guid StudentId { get; set; } + public Guid UserId { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStarted.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStarted.cs index b3601f543..2782797fb 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStarted.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStarted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Events.Application.Events diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStopped.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStopped.cs index 1194fac10..5470a3017 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStopped.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventBackgroundWorkerStopped.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Events.Application.Events diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventCreated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventCreated.cs index ab5b91378..eb182c085 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventCreated.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventCreated.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Application.Events { - public class EventCreated(Guid eventId, OrganizerType organizerType, Guid organizerId, IEnumerable mediaFilesIds) : IEvent + public class EventCreated(Guid eventId, OrganizerType organizerType, Guid organizerId, IEnumerable mediaFilesUrls) : IEvent { public Guid EventId { get; set; } = eventId; public OrganizerType OrganizerType { get; set; } = organizerType; public Guid OrganizerId { get; set; } = organizerId; - public IEnumerable MediaFilesIds { get; set; } = mediaFilesIds; + public IEnumerable MediaFilesUrls { get; set; } = mediaFilesUrls; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventDeleted.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventDeleted.cs index 9c74a3d37..fbcfd0d5c 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventDeleted.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventDeleted.cs @@ -1,11 +1,23 @@ using System; -using System.Collections.Generic; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events { - public class EventDeleted(Guid eventId) : IEvent + public class EventDeleted : IEvent { - public Guid EventId { get; set; } = eventId; + public Guid EventId { get; } + public string EventName { get; } + public Guid OrganizerId { get; } + public DateTime StartDate { get; } + public DateTime EndDate { get; } + + public EventDeleted(Guid eventId, string eventName, Guid organizerId, DateTime startDate, DateTime endDate) + { + EventId = eventId; + EventName = eventName; + OrganizerId = organizerId; + StartDate = startDate; + EndDate = endDate; + } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantAdded.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantAdded.cs index ceca1abd0..f85dba738 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantAdded.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantAdded.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events { @@ -8,13 +8,11 @@ public class EventParticipantAdded: IEvent { public Guid EventId { get; } public Guid ParticipantId { get; } - public string ParticipantName { get; } - public EventParticipantAdded(Guid eventId, Guid participantId, string participantName) + public EventParticipantAdded(Guid eventId, Guid participantId) { EventId = eventId; ParticipantId = participantId; - ParticipantName = participantName; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantRemoved.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantRemoved.cs index a45a5daf2..8bf72c2d1 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantRemoved.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventParticipantRemoved.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventUpdated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventUpdated.cs index f53f038f3..f758f2d14 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventUpdated.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventUpdated.cs @@ -1,8 +1,8 @@ using System; using System.Collections; using System.Collections.Generic; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Application.Events diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventViewed.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventViewed.cs index bb05e16b4..d896269f2 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventViewed.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventViewed.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventsStateUpdated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventsStateUpdated.cs index 7022d2562..fed7776f4 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventsStateUpdated.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/EventsStateUpdated.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/CommentCreated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/CommentCreated.cs index 521748421..272ac2cf8 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/CommentCreated.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/CommentCreated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Events.Application.Events.External diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/EventImageUploaded.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/EventImageUploaded.cs index a5450cddc..8785393b0 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/EventImageUploaded.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/EventImageUploaded.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Events.Application.Events.External diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/CommentCreatedHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/CommentCreatedHandler.cs index 326facb80..16fa03e23 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/CommentCreatedHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/CommentCreatedHandler.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Events.Application.Events.External; using MiniSpace.Services.Events.Core.Entities; using MiniSpace.Services.Events.Core.Repositories; @@ -25,8 +25,7 @@ public async Task HandleAsync(CommentCreated @event, CancellationToken cancellat { WriteIndented = true // Optional: For pretty-printing the JSON }); - Console.WriteLine("Received CommentCreated event:"); - Console.WriteLine(eventJson); + var comment = new Comment( @event.CommentId, diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/EventImageUploadedHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/EventImageUploadedHandler.cs index 4a813aa8d..eca574b97 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/EventImageUploadedHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/EventImageUploadedHandler.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Events.Core.Repositories; namespace MiniSpace.Services.Events.Application.Events.External.Handlers diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/MediaFileDeletedHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/MediaFileDeletedHandler.cs index 6470015a7..eaf7d88ca 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/MediaFileDeletedHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/MediaFileDeletedHandler.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Events.Core.Repositories; namespace MiniSpace.Services.Events.Application.Events.External.Handlers diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/ReactionCreatedHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/ReactionCreatedHandler.cs index e4967cbf3..b5cb92c69 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/ReactionCreatedHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/Handlers/ReactionCreatedHandler.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Events.Application.Events.External; using MiniSpace.Services.Events.Core.Entities; using MiniSpace.Services.Events.Core.Repositories; @@ -24,8 +24,6 @@ public async Task HandleAsync(ReactionCreated @event, CancellationToken cancella { WriteIndented = true }); - Console.WriteLine("Received ReactionCreated event:"); - Console.WriteLine(eventJson); var reaction = Reaction.Create( @event.ReactionId, diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/MediaFileDeleted.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/MediaFileDeleted.cs index a9f707f0a..e0e9a71aa 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/MediaFileDeleted.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/MediaFileDeleted.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events.External { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/ReactionCreated.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/ReactionCreated.cs index 0b4b0dfa1..d82e6d460 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/ReactionCreated.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/External/ReactionCreated.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events.External { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventParticipantRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventParticipantRejected.cs index 15d543c7e..5272d3a13 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventParticipantRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/AddEventParticipantRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelInterestInEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelInterestInEventRejected.cs index f479caa49..b0b7c8986 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelInterestInEventRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelInterestInEventRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelSignUpToEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelSignUpToEventRejected.cs index 1547ba748..0d0a5e616 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelSignUpToEventRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CancelSignUpToEventRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CreateEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CreateEventRejected.cs index b4881a57c..f4f2be842 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CreateEventRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/CreateEventRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.Mvc.RazorPages; using MiniSpace.Services.Events.Core.Entities; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/DeleteEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/DeleteEventRejected.cs index cd651d11b..98eb17d98 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/DeleteEventRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/DeleteEventRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RateEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RateEventRejected.cs index 0c3da7b1d..01ec8794c 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RateEventRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RateEventRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RemoveEventParticipantRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RemoveEventParticipantRejected.cs index 2713e235c..f97407499 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RemoveEventParticipantRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/RemoveEventParticipantRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchEventsRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchEventsRejected.cs index 79a3dd693..ecef627dc 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchEventsRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchEventsRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Events.Application.Events.Rejected diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchOrganizerEventsRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchOrganizerEventsRejected.cs index 58c133299..b66cbca39 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchOrganizerEventsRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SearchOrganizerEventsRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Events.Application.Events.Rejected diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/ShowInterestInEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/ShowInterestInEventRejected.cs index 02d1b1161..ac8a51967 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/ShowInterestInEventRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/ShowInterestInEventRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SignUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SignUpToEvent.cs index 8a0cf5029..915b75cc7 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SignUpToEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/SignUpToEvent.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/UpdateEventRejected.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/UpdateEventRejected.cs index e2aa4850f..195f8c545 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/UpdateEventRejected.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/Rejected/UpdateEventRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Events.Rejected { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledInterestInEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledInterestInEvent.cs index 6feb1f56c..ad85c4641 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledInterestInEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledInterestInEvent.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledSignUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledSignUpToEvent.cs index 8dd4e99ab..e96e87883 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledSignUpToEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentCancelledSignUpToEvent.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentShowedInterestInEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentShowedInterestInEvent.cs index 712e07a3b..2afff1612 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentShowedInterestInEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentShowedInterestInEvent.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentSignedUpToEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentSignedUpToEvent.cs index 0327fe518..c745ac0bc 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentSignedUpToEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Events/StudentSignedUpToEvent.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Events.Application.Events { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Extensions.cs index 5d68041b5..f01b0f873 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Extensions.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Extensions.cs @@ -1,6 +1,6 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Events.Application @@ -8,7 +8,7 @@ namespace MiniSpace.Services.Events.Application [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/MiniSpace.Services.Events.Application.csproj b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/MiniSpace.Services.Events.Application.csproj index 16556e065..7a5a01979 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/MiniSpace.Services.Events.Application.csproj +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/MiniSpace.Services.Events.Application.csproj @@ -6,11 +6,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEvent.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEvent.cs index f34284408..b74d7cf70 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEvent.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEvent.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; namespace MiniSpace.Services.Events.Application.Queries diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventParticipants.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventParticipants.cs index ecebf638e..7d51e3d2d 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventParticipants.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventParticipants.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; namespace MiniSpace.Services.Events.Application.Queries diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventRating.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventRating.cs index e1b02a8f1..61371f8ea 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventRating.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetEventRating.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; namespace MiniSpace.Services.Events.Application.Queries diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs index 03114a2cf..21d581a80 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedEvents.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Wrappers; using System.Collections.Generic; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs index c87907342..756264e44 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedOrganizerEvents.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Wrappers; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedUserViews.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedUserViews.cs index 4acc765f2..b79566652 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedUserViews.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetPaginatedUserViews.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Wrappers; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs index 50cbf6577..8eda40bf9 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetSearchEvents.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Wrappers; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs index a24bdf88a..8edd7874a 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEvents.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Wrappers; namespace MiniSpace.Services.Events.Application.Queries diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEventsFeed.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEventsFeed.cs index 16b8bd6da..7cefcfab9 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEventsFeed.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Queries/GetUserEventsFeed.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Wrappers; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventMapper.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventMapper.cs index b2a9e2e97..5b0309dfa 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IEventMapper.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Events.Core; namespace MiniSpace.Services.Events.Application.Services diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IMessageBroker.cs index 22731c6dc..e63bee917 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Application/Services/IMessageBroker.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Events.Application.Services { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs index fdde830ed..bb1ee9fd7 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Event.cs @@ -116,9 +116,9 @@ public void SignUpParticipant(Participant participant) public void AddParticipant(Participant participant) { - if (SignedUpParticipants.Any(p => p.StudentId == participant.StudentId)) + if (SignedUpParticipants.Any(p => p.UserId == participant.UserId)) { - throw new StudentAlreadySignedUpException(participant.StudentId, Id); + throw new StudentAlreadySignedUpException(participant.UserId, Id); } if (SignedUpParticipants.Count() >= Capacity) @@ -147,7 +147,7 @@ public void CancelSignUp(Guid studentId) public void RemoveParticipant(Guid studentId) { - var participant = _signedUpParticipants.SingleOrDefault(p => p.StudentId == studentId); + var participant = _signedUpParticipants.SingleOrDefault(p => p.UserId == studentId); if (participant is null) { throw new StudentNotSignedUpException(studentId, Id); @@ -180,9 +180,9 @@ public void AddGalleryImage(string newImageUrl) public void ShowParticipantInterest(Participant participant) { - if (InterestedParticipants.Any(p => p.StudentId == participant.StudentId)) + if (InterestedParticipants.Any(p => p.UserId == participant.UserId)) { - throw new StudentAlreadyInterestedInEventException(participant.StudentId, Id); + throw new StudentAlreadyInterestedInEventException(participant.UserId, Id); } _interestedParticipants.Add(participant); @@ -190,7 +190,7 @@ public void ShowParticipantInterest(Participant participant) public void CancelInterest(Guid studentId) { - var participant = _interestedParticipants.SingleOrDefault(p => p.StudentId == studentId); + var participant = _interestedParticipants.SingleOrDefault(p => p.UserId == studentId); if (participant is null) { throw new StudentNotInterestedInEventException(studentId, Id); @@ -206,7 +206,7 @@ public void Rate(Guid studentId, int rating) throw new InvalidEventState(Id, State.Archived, State); } - if (_signedUpParticipants.All(p => p.StudentId != studentId)) + if (_signedUpParticipants.All(p => p.UserId != studentId)) { throw new StudentNotSignedUpForEventException(Id, studentId); } @@ -216,7 +216,7 @@ public void Rate(Guid studentId, int rating) throw new InvalidRatingValueException(rating); } - if (_ratings.Any(r => r.StudentId == studentId)) + if (_ratings.Any(r => r.UserId == studentId)) { throw new StudentAlreadyRatedException(studentId, Id); } @@ -226,7 +226,7 @@ public void Rate(Guid studentId, int rating) public void CancelRate(Guid studentId) { - var rating = _ratings.SingleOrDefault(r => r.StudentId == studentId); + var rating = _ratings.SingleOrDefault(r => r.UserId == studentId); if (rating is null) { throw new StudentNotRatedEventException(studentId, Id); diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs index f215476a5..e764fa1b0 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Participant.cs @@ -2,8 +2,8 @@ namespace MiniSpace.Services.Events.Core.Entities { - public class Participant(Guid studentId) + public class Participant(Guid userId) { - public Guid StudentId { get; set; } = studentId; + public Guid UserId { get; set; } = userId; } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Rating.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Rating.cs index faa1449d2..1868d300c 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Rating.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Entities/Rating.cs @@ -2,9 +2,9 @@ namespace MiniSpace.Services.Events.Core.Entities { - public class Rating(Guid studentId, int value) + public class Rating(Guid userId, int value) { - public Guid StudentId { get; set; } = studentId; + public Guid UserId { get; set; } = userId; public int Value { get; set; } = value; } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyInterestedInEventException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyInterestedInEventException.cs index 808ff80ec..d69ef16db 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyInterestedInEventException.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyInterestedInEventException.cs @@ -6,12 +6,12 @@ public class StudentAlreadyInterestedInEventException : DomainException { public override string Code { get; } = "student_already_interested_in_event"; public Guid EventId { get; } - public Guid StudentId { get; } + public Guid UserId { get; } - public StudentAlreadyInterestedInEventException(Guid studentId, Guid eventId) - : base($"Student with ID: {studentId} is already interested in event with ID: {eventId}.") + public StudentAlreadyInterestedInEventException(Guid userId, Guid eventId) + : base($"Student with ID: {userId} is already interested in event with ID: {eventId}.") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyRatedException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyRatedException.cs index fec490b1f..3e56d6685 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyRatedException.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadyRatedException.cs @@ -5,13 +5,13 @@ namespace MiniSpace.Services.Events.Core.Exceptions public class StudentAlreadyRatedException : DomainException { public override string Code { get; } = "student_already_rated"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentAlreadyRatedException(Guid studentId, Guid eventId) : base( - $"Student with ID: {studentId} has already rated event with ID: {eventId}.") + public StudentAlreadyRatedException(Guid userId, Guid eventId) : base( + $"Student with ID: {userId} has already rated event with ID: {eventId}.") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadySignedUpException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadySignedUpException.cs index f737d16fb..202de6c46 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadySignedUpException.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentAlreadySignedUpException.cs @@ -5,13 +5,13 @@ namespace MiniSpace.Services.Events.Core.Exceptions public class StudentAlreadySignedUpException : DomainException { public override string Code { get; } = "student_already_signed_up"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentAlreadySignedUpException(Guid studentId, Guid eventId) : base( - $"Student with ID: {studentId} already signed up to event with ID: {eventId}.") + public StudentAlreadySignedUpException(Guid userId, Guid eventId) : base( + $"Student with ID: {userId} already signed up to event with ID: {eventId}.") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotInterestedInEventException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotInterestedInEventException.cs index 626d8269e..a225b0d46 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotInterestedInEventException.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotInterestedInEventException.cs @@ -5,13 +5,13 @@ namespace MiniSpace.Services.Events.Core.Exceptions public class StudentNotInterestedInEventException : DomainException { public override string Code { get; } = "student_is_not_interested_in_event"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentNotInterestedInEventException(Guid studentId, Guid eventId) - : base($"Student with ID: '{studentId}' is not interested in event with ID: '{eventId}'.") + public StudentNotInterestedInEventException(Guid userId, Guid eventId) + : base($"Student with ID: '{userId}' is not interested in event with ID: '{eventId}'.") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotRatedEventException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotRatedEventException.cs index 6023b304e..ea3be0554 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotRatedEventException.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotRatedEventException.cs @@ -6,13 +6,13 @@ public class StudentNotRatedEventException : DomainException { public override string Code { get; } = "student_not_rated_event"; public Guid EventId { get; } - public Guid StudentId { get; } + public Guid UserId { get; } - public StudentNotRatedEventException(Guid eventId, Guid studentId) - : base($"Student with ID: '{studentId}' has not rated event with ID: '{eventId}'.") + public StudentNotRatedEventException(Guid eventId, Guid userId) + : base($"Student with ID: '{userId}' has not rated event with ID: '{eventId}'.") { EventId = eventId; - StudentId = studentId; + UserId = userId; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpException.cs index da58daf97..716054648 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpException.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpException.cs @@ -5,13 +5,13 @@ namespace MiniSpace.Services.Events.Core.Exceptions public class StudentNotSignedUpException : DomainException { public override string Code { get; } = "student_not_signed_up"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentNotSignedUpException(Guid studentId, Guid eventId) - : base($"Student with ID: '{studentId}' has not signed up to event with ID: '{eventId}'.") + public StudentNotSignedUpException(Guid userId, Guid eventId) + : base($"Student with ID: '{userId}' has not signed up to event with ID: '{eventId}'.") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpForEventException.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpForEventException.cs index 1103c572d..f18111a2e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpForEventException.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Core/Exceptions/StudentNotSignedUpForEventException.cs @@ -6,13 +6,13 @@ public class StudentNotSignedUpForEventException : DomainException { public override string Code { get; } = "student_not_signed_for_event"; public Guid EventId { get; } - public Guid StudentId { get; } + public Guid UserId { get; } - public StudentNotSignedUpForEventException(Guid eventId, Guid studentId) - : base($"Student with ID: '{studentId}' is not signed for event with ID: '{eventId}'.") + public StudentNotSignedUpForEventException(Guid eventId, Guid userId) + : base($"Student with ID: '{userId}' is not signed for event with ID: '{eventId}'.") { EventId = eventId; - StudentId = studentId; + UserId = userId; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Contexts/AppContextFactory.cs index 365cdfa37..46951d441 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Events.Application; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 8dac5f368..aedc94fcb 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -2,10 +2,11 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Events.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index d4b6b90c4..aafd77c15 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -2,10 +2,11 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Events.Infrastructure.Decorators diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index e209a50c7..733bef3b8 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Events.Application.Commands; using MiniSpace.Services.Events.Application.Events.Rejected; using MiniSpace.Services.Events.Application.Exceptions; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 3272669cd..2d991a6e5 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -2,8 +2,8 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Events.Application.Exceptions; using MiniSpace.Services.Events.Core.Exceptions; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs index 0dd14dcdb..4b0efb20e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Extensions.cs @@ -3,28 +3,28 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -58,7 +58,7 @@ namespace MiniSpace.Services.Events.Infrastructure [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -110,7 +110,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseAuthentication() diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/Extensions.cs index 5c8325cf8..0b4eaec82 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/Extensions.cs @@ -1,5 +1,5 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; +using Paralax.CQRS.Logging; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Events.Application.Commands; using System.Diagnostics.CodeAnalysis; @@ -9,7 +9,7 @@ namespace MiniSpace.Services.Events.Infrastructure.Logging [ExcludeFromCodeCoverage] internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(DeleteEvent).Assembly; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 400d2cb91..34d0cdd8e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using MiniSpace.Services.Events.Application.Commands; using MiniSpace.Services.Events.Application.Events; using MiniSpace.Services.Events.Application.Events.External; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/MiniSpace.Services.Events.Infrastructure.csproj b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/MiniSpace.Services.Events.Infrastructure.csproj index 92b3b024b..c68599613 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/MiniSpace.Services.Events.Infrastructure.csproj +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/MiniSpace.Services.Events.Infrastructure.csproj @@ -6,35 +6,40 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/AddressDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/AddressDocument.cs index 5a65dc9db..eefc95296 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/AddressDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/AddressDocument.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/CommentDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/CommentDocument.cs index 1e8a23bdd..42c0c6502 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/CommentDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/CommentDocument.cs @@ -1,5 +1,5 @@ using System; -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs index 70d498ad8..b7d239ccc 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventDocument.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventSettingsDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventSettingsDocument.cs index 26c8867d8..a93651e2b 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventSettingsDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/EventSettingsDocument.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs index f1915a172..3dfb1126b 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/Extensions.cs @@ -32,9 +32,9 @@ public static EventDto AsDto(this EventDocument document, Guid studentId) UpdatedAt = document.UpdatedAt, Visibility = document.Visibility, Settings = document.Settings.AsDto(), - IsSignedUp = document.SignedUpStudents.Any(x => x.StudentId == studentId), - IsInterested = document.InterestedStudents.Any(x => x.StudentId == studentId), - StudentRating = document.Ratings.FirstOrDefault(x => x.StudentId == studentId)?.Value, + IsSignedUp = document.SignedUpStudents.Any(x => x.UserId == studentId), + IsInterested = document.InterestedStudents.Any(x => x.UserId == studentId), + StudentRating = document.Ratings.FirstOrDefault(x => x.UserId == studentId)?.Value, FriendsInterestedIn = Enumerable.Empty(), FriendsSignedUp = Enumerable.Empty() }; @@ -62,9 +62,9 @@ public static EventDto AsDto(this Event @event, Guid studentId) UpdatedAt = @event.UpdatedAt, Visibility = @event.Visibility, Settings = new EventSettingsDto(@event.Settings), - IsSignedUp = @event.SignedUpParticipants.Any(x => x.StudentId == studentId), - IsInterested = @event.InterestedParticipants.Any(x => x.StudentId == studentId), - StudentRating = @event.Ratings.FirstOrDefault(x => x.StudentId == studentId)?.Value, + IsSignedUp = @event.SignedUpParticipants.Any(x => x.UserId == studentId), + IsInterested = @event.InterestedParticipants.Any(x => x.UserId == studentId), + StudentRating = @event.Ratings.FirstOrDefault(x => x.UserId == studentId)?.Value, FriendsInterestedIn = Enumerable.Empty(), FriendsSignedUp = Enumerable.Empty() }; @@ -103,10 +103,10 @@ public static EventDto AsDtoWithFriends(this EventDocument document, Guid studen { var eventDto = document.AsDto(studentId); eventDto.FriendsInterestedIn = document.InterestedStudents - .Where(x => friends.Any(f => f.FriendId == x.StudentId)) + .Where(x => friends.Any(f => f.FriendId == x.UserId)) .Select(p => p.AsDto()); eventDto.FriendsSignedUp = document.SignedUpStudents - .Where(x => friends.Any(f => f.FriendId == x.StudentId)) + .Where(x => friends.Any(f => f.FriendId == x.UserId)) .Select(p => p.AsDto()); return eventDto; } @@ -148,7 +148,7 @@ public static ParticipantDto AsDto(this ParticipantDocument document) { return new ParticipantDto { - StudentId = document.StudentId + UserId = document.UserId }; } @@ -156,14 +156,14 @@ public static ParticipantDocument AsDocument(this Participant entity) { return new ParticipantDocument { - StudentId = entity.StudentId + UserId = entity.UserId }; } public static ParticipantDto ToDto(this ParticipantDocument document) => new ParticipantDto { - StudentId = document.StudentId, + UserId = document.UserId, }; @@ -229,17 +229,17 @@ public static EventSettings AsEntity(this EventSettingsDocument document) }; public static Participant AsEntity(this ParticipantDocument document) - => new (document.StudentId); + => new (document.UserId); public static RatingDto AsRatingDto(this RatingDocument document) => new () { - StudentId = document.StudentId, + StudentId = document.UserId, Value = document.Value }; public static Rating AsEntity(this RatingDocument document) - => new (document.StudentId, document.Value); + => new (document.UserId, document.Value); public static CommentDocument AsDocument(this Comment comment) diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/OrganizerDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/OrganizerDocument.cs index f720035ae..d92ca7cf8 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/OrganizerDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/OrganizerDocument.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ParticipantDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ParticipantDocument.cs index 3d0f385fa..dc1640973 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ParticipantDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ParticipantDocument.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Entities; @@ -9,19 +9,19 @@ namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents [ExcludeFromCodeCoverage] public class ParticipantDocument { - public Guid StudentId { get; set; } + public Guid UserId { get; set; } public static ParticipantDocument FromEntity(Participant participant) { return new ParticipantDocument { - StudentId = participant.StudentId, + UserId = participant.UserId, }; } public Participant ToEntity() { - return new Participant(StudentId); + return new Participant(UserId); } } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/RatingDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/RatingDocument.cs index 7de7e1c62..f9bb56392 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/RatingDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/RatingDocument.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents @@ -8,21 +8,21 @@ namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents [ExcludeFromCodeCoverage] public class RatingDocument { - public Guid StudentId { get; set; } + public Guid UserId { get; set; } public int Value { get; set; } public static RatingDocument FromEntity(Rating rating) { return new RatingDocument { - StudentId = rating.StudentId, + UserId = rating.UserId, Value = rating.Value }; } public Rating ToEntity() { - return new Rating(StudentId, Value); + return new Rating(UserId, Value); } } } \ No newline at end of file diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserCommentsDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserCommentsDocument.cs index 793024de0..e7679b312 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserCommentsDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserCommentsDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MongoDB.Bson.Serialization.Attributes; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserEventsViewsDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserEventsViewsDocument.cs index cffe5a1ec..77bfd7161 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserEventsViewsDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserEventsViewsDocument.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserReactionDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserReactionDocument.cs index 6549c7b7a..70fe6abc7 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserReactionDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/UserReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MongoDB.Bson.Serialization.Attributes; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ViewDocument.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ViewDocument.cs index 74a3f40ee..ec7bca54b 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ViewDocument.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Documents/ViewDocument.cs @@ -1,5 +1,5 @@ using System; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Events.Core.Entities; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs index 678331a12..b48acf41a 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventHandler.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Events.Application; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Events; @@ -20,19 +20,17 @@ namespace MiniSpace.Services.Events.Infrastructure.Mongo.Queries.Handlers public class GetEventHandler : IQueryHandler { private readonly IMongoRepository _eventRepository; - private readonly IFriendsServiceClient _friendsServiceClient; private readonly IAppContext _appContext; private readonly IMessageBroker _messageBroker; public GetEventHandler(IMongoRepository eventRepository, - IFriendsServiceClient friendsServiceClient, IAppContext appContext, IMessageBroker messageBroker) + IAppContext appContext, IMessageBroker messageBroker) { _eventRepository = eventRepository; - _friendsServiceClient = friendsServiceClient; _appContext = appContext; _messageBroker = messageBroker; } - + public async Task HandleAsync(GetEvent query, CancellationToken cancellationToken) { var document = await _eventRepository.GetAsync(p => p.Id == query.EventId); @@ -40,29 +38,13 @@ public async Task HandleAsync(GetEvent query, CancellationToken cancel { return null; } - - var identity = _appContext.Identity; - var friends = Enumerable.Empty(); - if (identity.IsAuthenticated) - { - try - { - var userFriends = await _friendsServiceClient.GetAsync(identity.Id); - if (userFriends != null && userFriends.Any()) - { - friends = userFriends.SelectMany(uf => uf.Friends); - } - } - catch (Exception ex) - { - Console.Error.WriteLine($"Error fetching friends: {ex.Message}"); - throw new ApplicationException("An error occurred while fetching friends data.", ex); - } - } + var identity = _appContext.Identity; await _messageBroker.PublishAsync(new EventViewed(query.EventId)); - return document.AsDtoWithFriends(identity.Id, friends); + + return document.AsDto(identity.Id); } } + } diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventParticipantsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventParticipantsHandler.cs index 2887d5953..9882b4a1b 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventParticipantsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventParticipantsHandler.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Events.Application; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Queries; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventRatingHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventRatingHandler.cs index c6b78a481..7ea0a3a0d 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventRatingHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetEventRatingHandler.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Queries; using MiniSpace.Services.Events.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedEventsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedEventsHandler.cs index a56403972..16d4a9859 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedEventsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedEventsHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Queries; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizerEventsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizerEventsHandler.cs index 242872cb4..1b1b615c5 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizerEventsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizerEventsHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Queries; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs index 14bffc25e..39e919a1e 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedSearchEventsHandler.cs @@ -4,7 +4,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Queries; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserViewsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserViewsHandler.cs index 06d9d7d90..c2e256e0d 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserViewsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserViewsHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Repositories; using MiniSpace.Services.Events.Core.Wrappers; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsFeedHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsFeedHandler.cs index f980a48cd..686c8cba7 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsFeedHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsFeedHandler.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Queries; using MiniSpace.Services.Events.Application.Services; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs index b477b3c0c..1722d4b1c 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Queries/Handlers/GetUserEventsHandler.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Events.Application; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Queries; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs index d0f83e0d9..522c48d2a 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventMongoRepository.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Wrappers; using MiniSpace.Services.Events.Core.Entities; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventsUserViewsRepository.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventsUserViewsRepository.cs index 615606031..72e62f13a 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventsUserViewsRepository.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/EventsUserViewsRepository.cs @@ -4,7 +4,7 @@ using MiniSpace.Services.Events.Core.Entities; using MiniSpace.Services.Events.Core.Repositories; using MiniSpace.Services.Events.Infrastructure.Mongo.Documents; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Repositories { diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs index d592e450f..8158d0ae5 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Mongo/Repositories/Extensions.cs @@ -7,7 +7,6 @@ using MiniSpace.Services.Events.Core.Entities; using MiniSpace.Services.Events.Infrastructure.Mongo.Documents; using MongoDB.Bson; -using MongoDB.Bson.Serialization; using MongoDB.Driver; namespace MiniSpace.Services.Events.Infrastructure.Mongo.Repositories @@ -156,13 +155,13 @@ public static FilterDefinition AddFriendsFilter (this FilterDefin if (friendsEngagementType != null) { filterDefinition &= friendsEngagementType == EventEngagementType.InterestedIn - ? FilterDefinitionBuilder.ElemMatch(x => x.InterestedStudents, s => friends.Contains(s.StudentId)) - : FilterDefinitionBuilder.ElemMatch(x => x.SignedUpStudents, s => friends.Contains(s.StudentId)); + ? FilterDefinitionBuilder.ElemMatch(x => x.InterestedStudents, s => friends.Contains(s.UserId)) + : FilterDefinitionBuilder.ElemMatch(x => x.SignedUpStudents, s => friends.Contains(s.UserId)); } else { - var interestedFilter = FilterDefinitionBuilder.ElemMatch(x => x.InterestedStudents, s => friends.Contains(s.StudentId)); - var signedUpFilter = FilterDefinitionBuilder.ElemMatch(x => x.SignedUpStudents, s => friends.Contains(s.StudentId)); + var interestedFilter = FilterDefinitionBuilder.ElemMatch(x => x.InterestedStudents, s => friends.Contains(s.UserId)); + var signedUpFilter = FilterDefinitionBuilder.ElemMatch(x => x.SignedUpStudents, s => friends.Contains(s.UserId)); filterDefinition &= FilterDefinitionBuilder.Or(interestedFilter, signedUpFilter); } @@ -172,13 +171,13 @@ public static FilterDefinition AddFriendsFilter (this FilterDefin public static FilterDefinition AddOrganizationsIdFilter(this FilterDefinition filterDefinition, IEnumerable organizationsEnumerable) { - var organizations = organizationsEnumerable.ToList(); + var organizations = organizationsEnumerable.Select(id => id.ToString()).ToList(); // Convert Guid to string if (organizations.Count > 0) { var organizationFilter = Builders.Filter.And( Builders.Filter.Ne(e => e.Organizer.OrganizationId, null), - Builders.Filter.In(e => (Guid)e.Organizer.OrganizationId, organizations) + Builders.Filter.In("Organizer.OrganizationId", organizations) ); filterDefinition &= organizationFilter; @@ -186,6 +185,7 @@ public static FilterDefinition AddOrganizationsIdFilter(this Filt return filterDefinition; } + public static FilterDefinition AddEventIdFilter(this FilterDefinition filterDefinition, IEnumerable eventIds) diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs index 89f5205ae..bb2f5c852 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/FriendsServiceClient.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Core.Wrappers; using MiniSpace.Services.Events.Application.Services.Clients; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs index f4a7f1977..d126f1e63 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/OrganizationsServiceClient.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Services.Clients; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/StudentsServiceClient.cs index c8896736b..98d92794d 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Events.Application.DTO; using MiniSpace.Services.Events.Application.Services.Clients; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/EventMapper.cs index 846034e1d..deb8393a0 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/EventMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Events.Application.Services; using MiniSpace.Services.Events.Core; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/MessageBroker.cs index db99528f0..9ef109c8c 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/MessageBroker.cs @@ -3,10 +3,10 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Workers/EventStateUpdaterWorker.cs b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Workers/EventStateUpdaterWorker.cs index 23499e7bf..8aefa0935 100644 --- a/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Workers/EventStateUpdaterWorker.cs +++ b/MiniSpace.Services.Events/src/MiniSpace.Services.Events.Infrastructure/Services/Workers/EventStateUpdaterWorker.cs @@ -2,8 +2,8 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Commands; +using Paralax.Persistence.MongoDB; using Microsoft.Extensions.Hosting; using MiniSpace.Services.Events.Application.Commands; using MiniSpace.Services.Events.Application.Events; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/MiniSpace.Services.Friends.Api.csproj b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/MiniSpace.Services.Friends.Api.csproj index d3ab4d661..78dc7cd27 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/MiniSpace.Services.Friends.Api.csproj +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/MiniSpace.Services.Friends.Api.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/Program.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/Program.cs index d700f1dfa..0bdf73934 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/Program.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Api/Program.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Queries; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Queries; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -19,6 +19,9 @@ // using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Friends.Application.Queries; using MiniSpace.Services.Friends.Infrastructure; +using Paralax.Types; +using Paralax.Core; + namespace MiniSpace.Services.Friends.Api { @@ -27,7 +30,7 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() @@ -49,18 +52,6 @@ public static async Task Main(string[] args) .Get>("friends/{userId}/following") .Put("friends/requests/{userId}/withdraw", afterDispatch: (cmd, ctx) => ctx.Response.Ok()) - - - // .Get>("friends/{studentId}", - // ctx => new GetFriends { StudentId = Guid.Parse(ctx.Request.RouteValues["studentId"].ToString()) }, - // (query, ctx) => ctx.Response.WriteAsJsonAsync(query), // Correctly define delegate with parameters - // afterDispatch: ctx => ctx.Response.Ok()) - - // .Get("friends/requests/sent", ctx => - // { - // var query = new GetSentFriendRequests { StudentId = ctx.User.GetUserId() }; - // return ctx.QueryDispatcher.QueryAsync(query); - // }, afterDispatch: ctx => ctx.Response.WriteAsJsonAsync(ctx.Result)) .Delete("friends/{requesterId}/{friendId}/remove") )) .UseLogging() diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/InviteFriendHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/InviteFriendHandler.cs index 4b7a042d4..fb022dbd8 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/InviteFriendHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/InviteFriendHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Friends.Application.Events.External; using MiniSpace.Services.Friends.Application.Exceptions; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendAcceptHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendAcceptHandler.cs index e2817a671..12276b0f3 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendAcceptHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendAcceptHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Friends.Core.Repositories; using MiniSpace.Services.Friends.Application.Exceptions; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendDeclineHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendDeclineHandler.cs index 4f1f685b3..228cfbed7 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendDeclineHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/PendingFriendDeclineHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Friends.Core.Repositories; using MiniSpace.Services.Friends.Application.Exceptions; using MiniSpace.Services.Friends.Application.Services; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/RemoveFriendHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/RemoveFriendHandler.cs index a59addd46..c9d9a1597 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/RemoveFriendHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/RemoveFriendHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Friends.Application.Events.External; using MiniSpace.Services.Friends.Application.Exceptions; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/SentFriendRequestWithdrawHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/SentFriendRequestWithdrawHandler.cs index e16fbcb98..31fc7b9a2 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/SentFriendRequestWithdrawHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/Handlers/SentFriendRequestWithdrawHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.Extensions.Logging; using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Friends.Application.Exceptions; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/InviteFriend.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/InviteFriend.cs index 24c2723c2..f8cf170c5 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/InviteFriend.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/InviteFriend.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Friends.Application.Commands { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendAccept.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendAccept.cs index 428b3f368..7c4793684 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendAccept.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendAccept.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Friends.Application.Commands { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendDecline.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendDecline.cs index 1d16eb063..800c961e4 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendDecline.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/PendingFriendDecline.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Friends.Application.Commands { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/RemoveFriend.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/RemoveFriend.cs index 0ca655626..be4132f28 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/RemoveFriend.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/RemoveFriend.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Friends.Application.Commands { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/SentFriendRequestWithdraw.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/SentFriendRequestWithdraw.cs index f888f2a8a..4ea08e0bf 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/SentFriendRequestWithdraw.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Commands/SentFriendRequestWithdraw.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Friends.Application.Commands diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/External/UserStatusUpdated.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/External/UserStatusUpdated.cs index 9bfbd9ebc..e4a47a73a 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/External/UserStatusUpdated.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/External/UserStatusUpdated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Friends.Application.Events.External { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendInvited.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendInvited.cs index 959d290e1..239c844c7 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendInvited.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendInvited.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using MiniSpace.Services.Friends.Core.Events; namespace MiniSpace.Services.Friends.Application.Events.External diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRemoved.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRemoved.cs index 75a03e4e5..dd8b3c470 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRemoved.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRemoved.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestCreated.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestCreated.cs index 31fbdcbca..5e983c891 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestCreated.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestCreated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Friends.Application.Events.External { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestSent.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestSent.cs index e486c6f25..930080f15 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestSent.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestSent.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Friends.Application.Events.External { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestWithdrawn.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestWithdrawn.cs index 3a791182d..6e440e28d 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestWithdrawn.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/FriendRequestWithdrawn.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Friends.Application.Events diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendAccepted.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendAccepted.cs index c18a19d4f..72d4b1237 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendAccepted.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendAccepted.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Friends.Application.Events.External { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendDeclined.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendDeclined.cs index 4b54c6a71..60e0aa849 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendDeclined.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/PendingFriendDeclined.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Friends.Application.Events.External { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendAddingFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendAddingFailed.cs index e03efdcc3..39090c86a 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendAddingFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendAddingFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendInvitationFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendInvitationFailed.cs index 05815ad1e..eb135d823 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendInvitationFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendInvitationFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRemovalFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRemovalFailed.cs index 5b0626268..6ee4f9881 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRemovalFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRemovalFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestCreationFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestCreationFailed.cs index d67190108..ad2a8e203 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestCreationFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestCreationFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestFailed.cs index 32e613a09..8c1125511 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestRejectionFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestRejectionFailed.cs index 62bee4652..d681d7815 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestRejectionFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestRejectionFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestSendingFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestSendingFailed.cs index fbd277af0..80bf65258 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestSendingFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/FriendRequestSendingFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendAcceptanceFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendAcceptanceFailed.cs index 8098732d6..47b39d09b 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendAcceptanceFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendAcceptanceFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendDeclinationFailed.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendDeclinationFailed.cs index 629837287..984394532 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendDeclinationFailed.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Events/Rejected/PendingFriendDeclinationFailed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Events.Rejected { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Extensions.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Extensions.cs index 4e24696d4..21885ef1a 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Extensions.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Extensions.cs @@ -1,12 +1,12 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application { public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/MiniSpace.Services.Friends.Application.csproj b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/MiniSpace.Services.Friends.Application.csproj index 6282c20bb..02318c05e 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/MiniSpace.Services.Friends.Application.csproj +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/MiniSpace.Services.Friends.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowers.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowers.cs index 3a6b8c730..5eb05f382 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowers.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowers.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Core.Wrappers; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowing.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowing.cs index 4a052a45d..da55311d2 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowing.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFollowing.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Core.Wrappers; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriend.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriend.cs index dcc6d4f35..0f98c435f 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriend.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriend.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; using System.Collections.Generic; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendEvents.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendEvents.cs index e889ea492..10b91ec6b 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendEvents.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendEvents.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; namespace MiniSpace.Services.Friends.Application.Queries diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendRequests.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendRequests.cs index dd9aa644e..b5f5501b8 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendRequests.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriendRequests.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using System.Text.Json.Serialization; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Core.Wrappers; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriends.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriends.cs index e3c0ee2d3..755efb168 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriends.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetFriends.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Core.Wrappers; using System.Collections.Generic; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetIncomingFriendRequests.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetIncomingFriendRequests.cs index ef72a4c6c..6916b4318 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetIncomingFriendRequests.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetIncomingFriendRequests.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Core.Wrappers; using System; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetSentFriendRequests.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetSentFriendRequests.cs index 566e36651..69a8ca0a9 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetSentFriendRequests.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Queries/GetSentFriendRequests.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Core.Wrappers; using System; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IEventMapper.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IEventMapper.cs index bd82e6c73..7223ad43e 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Friends.Application.Commands; using MiniSpace.Services.Friends.Core.Events; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IMessageBroker.cs index d7ce985c1..fa267b93d 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Friends.Application.Services { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Contexts/AppContextFactory.cs index 63c05b661..03af08d88 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Friends.Application; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index d81be523c..41f042ffb 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Friends.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 31f949391..46a75aca9 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Friends.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 8a8aacdc0..abf7f4636 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Friends.Application.Commands; using MiniSpace.Services.Friends.Application.Events.Rejected; using MiniSpace.Services.Friends.Application.Events.External; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index d3da6c0e8..dffcd5829 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Friends.Application.Exceptions; using MiniSpace.Services.Friends.Core.Exceptions; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Extensions.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Extensions.cs index b8cf933e6..ee441b688 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -41,13 +41,13 @@ using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Notifications.Infrastructure.Services.Clients; using MiniSpace.Services.Friends.Application.Services.Clients; -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Friends.Infrastructure { public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -92,7 +92,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 166feb9b5..cad775ac7 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,4 +1,4 @@ -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using MiniSpace.Services.Friends.Application.Commands; using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Friends.Application.Events.External; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/MiniSpace.Services.Friends.Infrastructure.csproj b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/MiniSpace.Services.Friends.Infrastructure.csproj index c0b3d159b..b6f69a85c 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/MiniSpace.Services.Friends.Infrastructure.csproj +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/MiniSpace.Services.Friends.Infrastructure.csproj @@ -7,29 +7,34 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendDocument.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendDocument.cs index a63b8ef00..5bca5ecda 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendDocument.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Friends.Core.Entities; namespace MiniSpace.Services.Friends.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendRequestDocument.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendRequestDocument.cs index 02e58ee06..0c9237efa 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendRequestDocument.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/FriendRequestDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Friends.Core.Entities; namespace MiniSpace.Services.Friends.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserFriendsDocument.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserFriendsDocument.cs index 3572c1c95..fd2a3a758 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserFriendsDocument.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserFriendsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Friends.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserRequestsDocument.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserRequestsDocument.cs index 0b30dd949..3135b76fb 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserRequestsDocument.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Documents/UserRequestsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Friends.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowersHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowersHandler.cs index f156041d5..a930fe03b 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowersHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowersHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Queries; using MiniSpace.Services.Friends.Core.Repositories; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowingHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowingHandler.cs index cebd0533f..4510dc8ce 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowingHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFollowingHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Queries; using MiniSpace.Services.Friends.Core.Repositories; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendHandler.cs index 4038f64f4..c551908dc 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Exceptions; using MiniSpace.Services.Friends.Application.Queries; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendRequestsHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendRequestsHandler.cs index 31ae8fbbb..a4bbc3d3b 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendRequestsHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendRequestsHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Queries; using MiniSpace.Services.Friends.Core.Entities; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendsHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendsHandler.cs index b6c94b422..9e2b58ef9 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendsHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetFriendsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Queries; using MiniSpace.Services.Friends.Core.Repositories; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetIncomingFriendRequestsHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetIncomingFriendRequestsHandler.cs index 7db86c872..ae929a118 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetIncomingFriendRequestsHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetIncomingFriendRequestsHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Queries; using MiniSpace.Services.Friends.Core.Wrappers; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetSentFriendRequestsHandler.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetSentFriendRequestsHandler.cs index 236f96111..55fdaef3a 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetSentFriendRequestsHandler.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Queries/Handlers/GetSentFriendRequestsHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Queries; using MiniSpace.Services.Friends.Core.Wrappers; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendMongoRepository.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendMongoRepository.cs index f662ceab1..f947fd0b0 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendMongoRepository.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Core.Entities; using MiniSpace.Services.Friends.Core.Repositories; using MiniSpace.Services.Friends.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendRequestMongoRepository.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendRequestMongoRepository.cs index 55406e03e..2de7dce46 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendRequestMongoRepository.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/FriendRequestMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Core.Entities; using MiniSpace.Services.Friends.Core.Repositories; using MiniSpace.Services.Friends.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentFriendsMongoRepository.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentFriendsMongoRepository.cs index 953e50e18..92d6f2061 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentFriendsMongoRepository.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentFriendsMongoRepository.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Core.Entities; using MiniSpace.Services.Friends.Core.Repositories; using MiniSpace.Services.Friends.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentRequestsMongoRepository.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentRequestsMongoRepository.cs index 36394b1a7..620d76018 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentRequestsMongoRepository.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/StudentRequestsMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Core.Entities; using MiniSpace.Services.Friends.Core.Repositories; using MiniSpace.Services.Friends.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/UserFriendsMongoRepository.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/UserFriendsMongoRepository.cs index 58037c4c5..30dddee41 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/UserFriendsMongoRepository.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Mongo/Repositories/UserFriendsMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Friends.Core.Entities; using MiniSpace.Services.Friends.Core.Repositories; using MiniSpace.Services.Friends.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/Clients/StudentsServiceClient.cs index e0432f70e..7ab57b7cc 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Friends.Application.Dto; using MiniSpace.Services.Friends.Application.Services.Clients; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/EventMapper.cs index 53e480d66..d0955df23 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Friends.Application.Events; using MiniSpace.Services.Friends.Application.Events.External; using MiniSpace.Services.Friends.Application.Services; diff --git a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/MessageBroker.cs index 0faf2333e..a42803c02 100644 --- a/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Friends/src/MiniSpace.Services.Friends.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/MiniSpace.Services.Identity.Api.csproj b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/MiniSpace.Services.Identity.Api.csproj index 3c7aac04b..08574d47f 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/MiniSpace.Services.Identity.Api.csproj +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/MiniSpace.Services.Identity.Api.csproj @@ -6,12 +6,14 @@ MiniSpace.Services.Identity.Api - - - - - - + + + @@ -21,4 +23,11 @@ + + + + + + + diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/Program.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/Program.cs index 4cc556507..9fa050295 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/Program.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Api/Program.cs @@ -1,11 +1,11 @@ using System; using System.Threading.Tasks; -using Convey; -using Convey.Auth; -using Convey.Secrets.Vault; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; +using Paralax; +using Paralax.Auth; +using Paralax.Secrets.Vault; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -15,6 +15,7 @@ using MiniSpace.Services.Identity.Application.Queries; using MiniSpace.Services.Identity.Application.Services; using MiniSpace.Services.Identity.Infrastructure; +using Paralax.Core; namespace MiniSpace.Services.Identity.Api { @@ -23,7 +24,7 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() @@ -127,13 +128,30 @@ public static async Task Main(string[] args) private static async Task GetUserAsync(Guid id, HttpContext context) { var user = await context.RequestServices.GetService().GetAsync(id); + Console.WriteLine(user.ToString()); if (user is null) { context.Response.StatusCode = 404; return; } - await context.Response.WriteJsonAsync(user); + await context.Response.WriteJsonAsync(new + { + Id = user.Id, // Ensure this is directly a Guid, not wrapped + user.Name, + user.Email, + Role = user.Role.ToString(), + user.CreatedAt, + Permissions = user.Permissions, + user.IsEmailVerified, + user.EmailVerifiedAt, + user.IsTwoFactorEnabled, + user.TwoFactorSecret, + user.IsOnline, + user.DeviceType, + user.LastActive, + user.IpAddress + }); } } } diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/BanUser.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/BanUser.cs index 727bce93c..d8cf51844 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/BanUser.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/BanUser.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Identity.Application.Commands { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/DisableTwoFactor.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/DisableTwoFactor.cs index a4c5706da..0ba05eddb 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/DisableTwoFactor.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/DisableTwoFactor.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Identity.Application.Commands { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/EnableTwoFactor.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/EnableTwoFactor.cs index df091d6d4..703a35999 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/EnableTwoFactor.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/EnableTwoFactor.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Identity.Application.Commands { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/ForgotPassword.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/ForgotPassword.cs index 2a3198712..5029a366a 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/ForgotPassword.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/ForgotPassword.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Identity.Application.Commands { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/GenerateTwoFactorSecret.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/GenerateTwoFactorSecret.cs index 6c5c52999..0e9476190 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/GenerateTwoFactorSecret.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/GenerateTwoFactorSecret.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Identity.Application.Commands { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/DisableTwoFactorHandler.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/DisableTwoFactorHandler.cs index ca366c12a..7769af2f6 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/DisableTwoFactorHandler.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/DisableTwoFactorHandler.cs @@ -1,7 +1,8 @@ using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Identity.Application.Services; using Microsoft.Extensions.Logging; +using System.Threading; namespace MiniSpace.Services.Identity.Application.Commands.Handlers { @@ -16,7 +17,7 @@ public DisableTwoFactorHandler(IIdentityService identityService, ILogger _identityService.SignUpAsync(command); + public Task HandleAsync(SignUp command, CancellationToken cancellationToken) => _identityService.SignUpAsync(command); } } \ No newline at end of file diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/VerifyEmailHandler.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/VerifyEmailHandler.cs index eb3b3c344..8b098157a 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/VerifyEmailHandler.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Commands/Handlers/VerifyEmailHandler.cs @@ -1,7 +1,9 @@ using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Identity.Application.Services; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using System.Threading; namespace MiniSpace.Services.Identity.Application.Commands.Handlers { @@ -16,7 +18,7 @@ public VerifyEmailHandler(IIdentityService identityService, ILogger builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/MiniSpace.Services.Identity.Application.csproj b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/MiniSpace.Services.Identity.Application.csproj index f42419f97..0fa69a49f 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/MiniSpace.Services.Identity.Application.csproj +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/MiniSpace.Services.Identity.Application.csproj @@ -5,16 +5,28 @@ - - - - - + + + + + + + + + + + + diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Queries/GetOnlineUsers.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Queries/GetOnlineUsers.cs new file mode 100644 index 000000000..b0c3a37fb --- /dev/null +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Queries/GetOnlineUsers.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using Paralax.CQRS.Queries; +using MiniSpace.Services.Identity.Application.DTO; + +namespace MiniSpace.Services.Identity.Application.Queries +{ + /// + /// Very unsafe query! + /// + /// + /// Handler the statuses update withing the corresponding evnet + /// subscriber and handler in the User service. + /// + public class GetOnlineUsers : IQuery> + { + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 10; + } +} diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Queries/GetUser.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Queries/GetUser.cs index b4e57f067..f63bca107 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Queries/GetUser.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Queries/GetUser.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Identity.Application.DTO; namespace MiniSpace.Services.Identity.Application.Queries diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/IIPAddressService.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/IIPAddressService.cs new file mode 100644 index 000000000..8d685a5c2 --- /dev/null +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/IIPAddressService.cs @@ -0,0 +1,10 @@ +using System.Net; +using Microsoft.AspNetCore.Http; + +namespace MiniSpace.Services.Identity.Application.Services +{ + public interface IIPAddressService + { + string GetIPAddress(); + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/IMessageBroker.cs index 720012fe6..8afd3714b 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/IMessageBroker.cs @@ -1,5 +1,5 @@ using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Identity.Application.Services { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/IdentityService.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/IdentityService.cs index 2c68cb59f..e39f00c69 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/IdentityService.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/IdentityService.cs @@ -8,7 +8,6 @@ using MiniSpace.Services.Identity.Application.Commands; using MiniSpace.Services.Identity.Application.DTO; using MiniSpace.Services.Identity.Application.Events; -using MiniSpace.Services.Identity.Application.Events.Rejected; using MiniSpace.Services.Identity.Application.Exceptions; using MiniSpace.Services.Identity.Core.Entities; using MiniSpace.Services.Identity.Core.Exceptions; @@ -33,13 +32,19 @@ public class IdentityService : IIdentityService private readonly ITwoFactorSecretTokenService _twoFactorSecretTokenService; private readonly ITwoFactorCodeService _twoFactorCodeService; private readonly ILogger _logger; - - public IdentityService(IUserRepository userRepository, IPasswordService passwordService, - IJwtProvider jwtProvider, IRefreshTokenService refreshTokenService, - IMessageBroker messageBroker, IUserResetTokenRepository userResetTokenRepository, + private readonly IIPAddressService _ipAddressService; + + public IdentityService( + IUserRepository userRepository, + IPasswordService passwordService, + IJwtProvider jwtProvider, + IRefreshTokenService refreshTokenService, + IMessageBroker messageBroker, + IUserResetTokenRepository userResetTokenRepository, IVerificationTokenService verificationTokenService, ITwoFactorSecretTokenService twoFactorSecretTokenService, - ITwoFactorCodeService twoFactorCodeService, + ITwoFactorCodeService twoFactorCodeService, + IIPAddressService ipAddressService, ILogger logger) { _userRepository = userRepository ?? throw new ArgumentNullException(nameof(userRepository)); @@ -51,17 +56,21 @@ public IdentityService(IUserRepository userRepository, IPasswordService password _verificationTokenService = verificationTokenService ?? throw new ArgumentNullException(nameof(verificationTokenService)); _twoFactorSecretTokenService = twoFactorSecretTokenService ?? throw new ArgumentNullException(nameof(twoFactorSecretTokenService)); _twoFactorCodeService = twoFactorCodeService ?? throw new ArgumentNullException(nameof(twoFactorCodeService)); + _ipAddressService = ipAddressService ?? throw new ArgumentNullException(nameof(ipAddressService)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public async Task GetAsync(Guid id) { var user = await _userRepository.GetAsync(id); + Console.WriteLine(JsonSerializer.Serialize(user)); return user is null ? null : new UserDto(user); } public async Task SignInAsync(SignIn command) { + var ipAddress = _ipAddressService.GetIPAddress(); + if (!EmailRegex.IsMatch(command.Email)) { throw new InvalidEmailException(command.Email); @@ -95,20 +104,22 @@ public async Task SignInAsync(SignIn command) claims.Add("permissions", user.Permissions); } - user.SetOnlineStatus(true, command.DeviceType); + user.SetOnlineStatus(true, command.DeviceType, ipAddress); await _userRepository.UpdateAsync(user); var auth = _jwtProvider.Create(user.Id, user.Role, claims: claims); auth.RefreshToken = await _refreshTokenService.CreateAsync(user.Id); - + auth.UserId = user.Id; auth.IsOnline = true; auth.DeviceType = command.DeviceType; + auth.IpAddress = ipAddress; - await _messageBroker.PublishAsync(new SignedIn(user.Id, user.Role)); + await _messageBroker.PublishAsync(new SignedIn(user.Id, user.Role.ToString(), command.DeviceType, ipAddress)); return auth; } + public async Task SignUpAsync(SignUp command) { if (!EmailRegex.IsMatch(command.Email)) @@ -118,7 +129,7 @@ public async Task SignUpAsync(SignUp command) } var user = await _userRepository.GetAsync(command.Email); - if (user is {}) + if (user is not null) { _logger.LogError($"Email already in use: {command.Email}"); throw new EmailInUseException(command.Email); @@ -244,7 +255,6 @@ public async Task VerifyEmailAsync(VerifyEmail command) await _messageBroker.PublishAsync(new EmailVerified(user.Id, user.Email, DateTime.UtcNow)); } - public async Task EnableTwoFactorAsync(EnableTwoFactor command) { var user = await _userRepository.GetAsync(command.UserId); @@ -320,23 +330,36 @@ public async Task VerifyTwoFactorCodeAsync(VerifyTwoFactorCode command) var auth = _jwtProvider.Create(user.Id, user.Role, claims: claims); auth.RefreshToken = await _refreshTokenService.CreateAsync(user.Id); - await _messageBroker.PublishAsync(new SignedIn(user.Id, user.Role)); + auth.DeviceType = command.DeviceType; + auth.IpAddress = _ipAddressService.GetIPAddress(); + + await _messageBroker.PublishAsync(new SignedIn(user.Id, user.Role.ToString(), command.DeviceType, auth.IpAddress)); + return auth; } public async Task UpdateUserStatusAsync(UpdateUserStatus command) { var user = await _userRepository.GetAsync(command.UserId); + if (user == null) { + _logger.LogError($"User with id {command.UserId} not found."); throw new UserNotFoundException(command.UserId); } - user.SetOnlineStatus(command.IsOnline, command.DeviceType); - await _userRepository.UpdateAsync(user); + try + { + user.SetOnlineStatus(command.IsOnline, command.DeviceType); + user.UpdateLastActive(); - _logger.LogInformation($"Updated status for user {command.UserId}: Online = {command.IsOnline}, DeviceType = {command.DeviceType}"); + await _userRepository.UpdateAsync(user); + } + catch (Exception ex) + { + _logger.LogError($"Error updating status for user {command.UserId}: {ex.Message}"); + throw; + } } - } } diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/RefreshTokenService.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/RefreshTokenService.cs index 0af9993a6..4de69fff2 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/RefreshTokenService.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Application/Services/Identity/RefreshTokenService.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using MiniSpace.Services.Identity.Application.DTO; +using MiniSpace.Services.Identity.Application.Events; using MiniSpace.Services.Identity.Application.Exceptions; using MiniSpace.Services.Identity.Core.Entities; using MiniSpace.Services.Identity.Core.Exceptions; @@ -16,14 +17,23 @@ public class RefreshTokenService : IRefreshTokenService private readonly IUserRepository _userRepository; private readonly IJwtProvider _jwtProvider; private readonly IRng _rng; + private readonly IMessageBroker _messageBroker; + private readonly IIPAddressService _ipAddressService; - public RefreshTokenService(IRefreshTokenRepository refreshTokenRepository, - IUserRepository userRepository, IJwtProvider jwtProvider, IRng rng) + public RefreshTokenService( + IRefreshTokenRepository refreshTokenRepository, + IUserRepository userRepository, + IJwtProvider jwtProvider, + IRng rng, + IMessageBroker messageBroker, + IIPAddressService ipAddressService) { _refreshTokenRepository = refreshTokenRepository; _userRepository = userRepository; _jwtProvider = jwtProvider; _rng = rng; + _messageBroker = messageBroker; + _ipAddressService = ipAddressService; } public async Task CreateAsync(Guid userId) @@ -49,49 +59,38 @@ public async Task RevokeAsync(string refreshToken) var user = await _userRepository.GetAsync(token.UserId); if (user != null) { - user.SetOnlineStatus(false, null); + user.SetOnlineStatus(false, null); await _userRepository.UpdateAsync(user); + + await _messageBroker.PublishAsync(new SignedOut(user.Id, user.DeviceType)); } } public async Task UseAsync(string refreshToken) { var token = await _refreshTokenRepository.GetAsync(refreshToken); - User user = null; - if (token is null || token.Revoked) { - if (token?.UserId != null) - { - user = await _userRepository.GetAsync(token.UserId); - } - - if (user != null) - { - user.SetOnlineStatus(false, null); - await _userRepository.UpdateAsync(user); - } - throw new InvalidRefreshTokenException(); } - user = await _userRepository.GetAsync(token.UserId); + var user = await _userRepository.GetAsync(token.UserId); if (user is null) { throw new UserNotFoundException(token.UserId); } var claims = user.Permissions.Any() - ? new Dictionary> - { - ["permissions"] = user.Permissions - } + ? new Dictionary> { ["permissions"] = user.Permissions } : null; var auth = _jwtProvider.Create(token.UserId, user.Role, claims: claims); auth.RefreshToken = refreshToken; + var ipAddress = _ipAddressService.GetIPAddress(); + await _messageBroker.PublishAsync(new TokenRefreshed(user.Id, user.DeviceType, ipAddress)); + return auth; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Core/Entities/User.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Core/Entities/User.cs index fc28cf090..ce65f3b11 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Core/Entities/User.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Core/Entities/User.cs @@ -22,6 +22,9 @@ public class User : AggregateRoot public string DeviceType { get; private set; } public DateTime? LastActive { get; private set; } + // Private setter to prevent direct assignment + public string IpAddress { get; private set; } + public User(Guid id, string name, string email, string password, Role role, DateTime createdAt, IEnumerable permissions = null) { @@ -55,7 +58,8 @@ public User(Guid id, string name, string email, string password, Role role, Date IsOnline = false; DeviceType = null; - LastActive = DateTime.UtcNow; + LastActive = DateTime.UtcNow; + IpAddress = null; // IpAddress starts as null } internal User(Guid id, string name, string email, string password, Role role, DateTime createdAt, @@ -70,13 +74,21 @@ internal User(Guid id, string name, string email, string password, Role role, Da TwoFactorSecret = twoFactorSecret; } - public void SetOnlineStatus(bool isOnline, string deviceType) + public void SetOnlineStatus(bool isOnline, string deviceType, string ipAddress) { IsOnline = isOnline; DeviceType = isOnline ? deviceType : null; LastActive = DateTime.UtcNow; + SetIpAddress(ipAddress); } + public void SetOnlineStatus(bool isOnline, string deviceType) + { + IsOnline = isOnline; + DeviceType = isOnline ? deviceType : null; + LastActive = DateTime.UtcNow; + } + public void UpdateLastActive() { LastActive = DateTime.UtcNow; @@ -150,6 +162,17 @@ public void SetTwoFactorSecret(string secret) { TwoFactorSecret = secret; } + + // New method to set the IP address, throwing an exception if it's invalid + public void SetIpAddress(string ipAddress) + { + if (string.IsNullOrWhiteSpace(ipAddress)) + { + throw new InvalidIpAddressException("IP address cannot be null or empty."); + } + + IpAddress = ipAddress; + } } public static class UserPermissions diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Core/Exceptions/InvalidIpAddressException.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Core/Exceptions/InvalidIpAddressException.cs new file mode 100644 index 000000000..08e4fa27e --- /dev/null +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Core/Exceptions/InvalidIpAddressException.cs @@ -0,0 +1,15 @@ +namespace MiniSpace.Services.Identity.Core.Exceptions +{ + public class InvalidIpAddressException : DomainException + { + public override string Code { get; } = "invalid_ip_address"; + + public InvalidIpAddressException() : base("IP address cannot be null or empty.") + { + } + + public InvalidIpAddressException(string message) : base(message) + { + } + } +} diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Auth/JwtProvider.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Auth/JwtProvider.cs index 109ede39b..935a775ff 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Auth/JwtProvider.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Auth/JwtProvider.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Auth; +using Paralax.Auth; using MiniSpace.Services.Identity.Application.DTO; using MiniSpace.Services.Identity.Application.Services; using MiniSpace.Services.Identity.Core.Entities; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Contexts/AppContextFactory.cs index f22a58396..8469585f6 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Identity.Application; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index dd68793d8..1495eab60 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,10 +1,12 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Identity.Infrastructure.Decorators { @@ -31,7 +33,7 @@ public OutboxCommandHandlerDecorator(ICommandHandler handler, IMessage : messageProperties.MessageId; } - public Task HandleAsync(TCommand command) + public Task HandleAsync(TCommand command, CancellationToken cancellationToken) => _enabled ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(command)) : _handler.HandleAsync(command); diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 8855a76f4..10948c654 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,10 +1,12 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Identity.Infrastructure.Decorators { @@ -31,7 +33,7 @@ public OutboxEventHandlerDecorator(IEventHandler handler, IMessageOutbox : messageProperties.MessageId; } - public Task HandleAsync(TEvent @event) + public Task HandleAsync(TEvent @event, CancellationToken cancellationToken) => _enabled ? _outbox.HandleAsync(_messageId, () => _handler.HandleAsync(@event)) : _handler.HandleAsync(@event); diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 561fc75ca..37fcf41fb 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Identity.Application.Commands; using MiniSpace.Services.Identity.Application.Events.Rejected; using MiniSpace.Services.Identity.Application.Exceptions; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 11e970513..66fa7007d 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -2,8 +2,8 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Identity.Application.Exceptions; using MiniSpace.Services.Identity.Core.Exceptions; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Extensions.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Extensions.cs index 2c57a3081..f02424721 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Extensions.cs @@ -3,29 +3,29 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -using Convey; -using Convey.Auth; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.Auth; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; @@ -47,13 +47,15 @@ using MiniSpace.Services.Identity.Infrastructure.Mongo.Repositories; using MiniSpace.Services.Identity.Infrastructure.Services; using System.Diagnostics.CodeAnalysis; +using Paralax.CQRS.WebApi; +using Paralax; namespace MiniSpace.Services.Identity.Infrastructure { [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -62,6 +64,7 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) builder.Services.AddTransient(); builder.Services.AddScoped(); builder.Services.AddSingleton(); + builder.Services.AddScoped(); builder.Services.AddSingleton, PasswordHasher>(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -75,6 +78,7 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); return builder + .AddErrorHandler() .AddQueryHandlers() .AddInMemoryQueryDispatcher() @@ -100,14 +104,15 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app { app.UseErrorHandler() .UseSwaggerDocs() - .UseJaeger() - .UseConvey() + .UseParalax() + // .UseJaegerTracing() .UseAccessTokenValidator() .UseMongo() .UsePublicContracts() .UseMetrics() .UseAuthentication() .UseRabbitMq() + .SubscribeCommand() .SubscribeCommand(); return app; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/MiniSpace.Services.Identity.Infrastructure.csproj b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/MiniSpace.Services.Identity.Infrastructure.csproj index 6fa6050eb..eaccd5559 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/MiniSpace.Services.Identity.Infrastructure.csproj +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/MiniSpace.Services.Identity.Infrastructure.csproj @@ -4,31 +4,65 @@ net8.0 + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs index d3b201985..da8051746 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/Extensions.cs @@ -21,12 +21,14 @@ public static User AsEntity(this UserDocument document) TwoFactorSecret = document.TwoFactorSecret }; + // Setting the online status and IP address user.SetOnlineStatus(document.IsOnline, document.DeviceType); - user.UpdateLastActive(); + user.UpdateLastActive(); return user; } + // Convert User Entity to UserDocument public static UserDocument AsDocument(this User entity) => new UserDocument { @@ -44,7 +46,9 @@ public static UserDocument AsDocument(this User entity) TwoFactorSecret = entity.TwoFactorSecret, IsOnline = entity.IsOnline, DeviceType = entity.DeviceType, - LastActive = entity.LastActive + LastActive = entity.LastActive, + + IpAddress = entity.IpAddress }; public static UserDto AsDto(this UserDocument document) @@ -62,12 +66,16 @@ public static UserDto AsDto(this UserDocument document) TwoFactorSecret = document.TwoFactorSecret, IsOnline = document.IsOnline, DeviceType = document.DeviceType, - LastActive = document.LastActive + LastActive = document.LastActive, + + IpAddress = document.IpAddress }; + // Convert RefreshTokenDocument to RefreshToken Entity public static RefreshToken AsEntity(this RefreshTokenDocument document) => new RefreshToken(document.Id, document.UserId, document.Token, document.CreatedAt, document.RevokedAt); + // Convert RefreshToken Entity to RefreshTokenDocument public static RefreshTokenDocument AsDocument(this RefreshToken entity) => new RefreshTokenDocument { @@ -78,6 +86,7 @@ public static RefreshTokenDocument AsDocument(this RefreshToken entity) RevokedAt = entity.RevokedAt }; + // Convert UserResetToken Entity to UserResetTokenDto public static UserResetTokenDto AsDto(this UserResetToken userResetToken) => new UserResetTokenDto { @@ -86,14 +95,16 @@ public static UserResetTokenDto AsDto(this UserResetToken userResetToken) ResetTokenExpires = userResetToken.ResetTokenExpires }; - public static UserResetToken AsEntity(this UserResetTokenDocument document) + // Convert UserResetTokenDocument to UserResetToken Entity + public static UserResetToken AsEntity(this UserResetTokenDocument document) => new UserResetToken( document.UserId, document.ResetToken, document.ResetTokenExpires ?? DateTime.MinValue ); - public static UserResetTokenDocument AsDocument(this UserResetToken userResetToken) + // Convert UserResetToken Entity to UserResetTokenDocument + public static UserResetTokenDocument AsDocument(this UserResetToken userResetToken) { if (userResetToken == null) { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/RefreshTokenDocument.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/RefreshTokenDocument.cs index a18d13a78..51f089275 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/RefreshTokenDocument.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/RefreshTokenDocument.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Identity.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserDocument.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserDocument.cs index ebf458716..d70e7ed9b 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserDocument.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserDocument.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Identity.Infrastructure.Mongo.Documents { @@ -24,5 +24,6 @@ internal sealed class UserDocument : IIdentifiable public bool IsOnline { get; set; } public string DeviceType { get; set; } public DateTime? LastActive { get; set; } + public string IpAddress { get; set; } } } diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserResetTokenDocument.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserResetTokenDocument.cs index 3b17d0f17..1f9624451 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserResetTokenDocument.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Documents/UserResetTokenDocument.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Identity.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Extensions.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Extensions.cs index 992ac7634..43154ea29 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Extensions.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Extensions.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Queries/Handlers/GetOnlineUsersHandler.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Queries/Handlers/GetOnlineUsersHandler.cs new file mode 100644 index 000000000..1912342aa --- /dev/null +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Queries/Handlers/GetOnlineUsersHandler.cs @@ -0,0 +1,47 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; +using MiniSpace.Services.Identity.Application.DTO; +using MiniSpace.Services.Identity.Application.Queries; +using MiniSpace.Services.Identity.Infrastructure.Mongo.Documents; +using MongoDB.Driver; +using System.Threading; + +namespace MiniSpace.Services.Identity.Infrastructure.Mongo.Queries.Handlers +{ + internal sealed class GetOnlineUsersHandler : IQueryHandler> + { + private readonly IMongoRepository _userRepository; + + public GetOnlineUsersHandler(IMongoRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task> HandleAsync(GetOnlineUsers query, CancellationToken cancellationToken) + { + var filter = Builders.Filter.Eq(u => u.IsOnline, true); + + var totalResults = await _userRepository.Collection.CountDocumentsAsync(filter); + + var totalPages = (int)Math.Ceiling(totalResults / (double)query.PageSize); + + var onlineUsers = await _userRepository.Collection + .Find(filter) + .SortByDescending(u => u.LastActive) + .Skip((query.Page - 1) * query.PageSize) + .Limit(query.PageSize) + .ToListAsync(); + + return PagedResult.Create( + onlineUsers.Select(user => user.AsDto()), + query.Page, + query.PageSize, + totalPages, + totalResults + ); + } + } +} diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Queries/Handlers/GetUserHandler.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Queries/Handlers/GetUserHandler.cs index c9a641cb8..cbce54b32 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Queries/Handlers/GetUserHandler.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Queries/Handlers/GetUserHandler.cs @@ -1,11 +1,12 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Identity.Application.DTO; using MiniSpace.Services.Identity.Application.Queries; using MiniSpace.Services.Identity.Infrastructure.Mongo.Documents; +using System.Threading; namespace MiniSpace.Services.Identity.Infrastructure.Mongo.Queries.Handlers { @@ -19,7 +20,7 @@ public GetUserHandler(IMongoRepository userRepository) _userRepository = userRepository; } - public async Task HandleAsync(GetUser query) + public async Task HandleAsync(GetUser query, CancellationToken cancellationToken) { var user = await _userRepository.GetAsync(query.UserId); diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs index ff4de6e46..310433754 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/RefreshTokenRepository.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Identity.Core.Entities; using MiniSpace.Services.Identity.Core.Repositories; using MiniSpace.Services.Identity.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs index ca552d2e3..f8e2ea547 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserRepository.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Identity.Core.Entities; using MiniSpace.Services.Identity.Core.Repositories; using MiniSpace.Services.Identity.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserResetTokenRepository.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserResetTokenRepository.cs index 2d42918e5..9531e0587 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserResetTokenRepository.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Mongo/Repositories/UserResetTokenRepository.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Identity.Core.Entities; using MiniSpace.Services.Identity.Core.Repositories; using MiniSpace.Services.Identity.Application.Exceptions; diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Services/IPAddressService.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Services/IPAddressService.cs new file mode 100644 index 000000000..c20f64cbc --- /dev/null +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Services/IPAddressService.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.AspNetCore.Http; + +namespace MiniSpace.Services.Identity.Application.Services +{ + public class IPAddressService : IIPAddressService + { + private readonly IHttpContextAccessor _httpContextAccessor; + + public IPAddressService(IHttpContextAccessor httpContextAccessor) + { + _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); + } + + public string GetIPAddress() + { + var context = _httpContextAccessor.HttpContext; + if (context == null) + { + return "Unknown IP"; + } + + var forwardedHeader = context.Request.Headers["X-Forwarded-For"].ToString(); + if (!string.IsNullOrEmpty(forwardedHeader)) + { + var ipAddresses = forwardedHeader.Split(','); + if (ipAddresses.Length > 0) + { + return ipAddresses[0].Trim(); + } + } + + return context.Connection.RemoteIpAddress?.ToString() ?? "Unknown IP"; + } + } +} diff --git a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Services/MessageBroker.cs index 15e44d1d4..023a8e301 100644 --- a/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Identity/src/MiniSpace.Services.Identity.Infrastructure/Services/MessageBroker.cs @@ -3,10 +3,10 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Application.UnitTests/Services/IdentityServiceTests.cs b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Application.UnitTests/Services/IdentityServiceTests.cs index d8c0950aa..cd1b439d8 100644 --- a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Application.UnitTests/Services/IdentityServiceTests.cs +++ b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Application.UnitTests/Services/IdentityServiceTests.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.Extensions.Logging; using MiniSpace.Services.Identity.Application.Commands; using MiniSpace.Services.Identity.Application.Events; diff --git a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/AggregatedIdTest.cs b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/AggregatedIdTest.cs index adab4469b..220e51ff9 100644 --- a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/AggregatedIdTest.cs +++ b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/AggregatedIdTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Identity.Application.Commands.Handlers; using MiniSpace.Services.Identity.Application.Commands; using MiniSpace.Services.Identity.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RefreshTokenTest.cs b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RefreshTokenTest.cs index 336eea4e2..0cddc35da 100644 --- a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RefreshTokenTest.cs +++ b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RefreshTokenTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Identity.Application.Commands.Handlers; using MiniSpace.Services.Identity.Application.Commands; using MiniSpace.Services.Identity.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RoleTest.cs b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RoleTest.cs index bb0406cf1..6477ca714 100644 --- a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RoleTest.cs +++ b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/RoleTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Identity.Application.Commands.Handlers; using MiniSpace.Services.Identity.Application.Commands; using MiniSpace.Services.Identity.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/UserTest.cs b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/UserTest.cs index b53ddc96b..158e24e9b 100644 --- a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/UserTest.cs +++ b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Core.UnitTests/Entities/UserTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Identity.Application.Commands.Handlers; using MiniSpace.Services.Identity.Application.Commands; using MiniSpace.Services.Identity.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Infrastructure.UnitTests/Services/MessageBrokerTests.cs b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Infrastructure.UnitTests/Services/MessageBrokerTests.cs index b30f600bf..bb9fce535 100644 --- a/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Infrastructure.UnitTests/Services/MessageBrokerTests.cs +++ b/MiniSpace.Services.Identity/tests/MiniSpace.Services.Identity.Infrastructure.UnitTests/Services/MessageBrokerTests.cs @@ -1,12 +1,12 @@ // using Xunit; // using Moq; -// using Convey.CQRS.Events; +// using Paralax.CQRS.Events; // using System; // using System.Collections.Generic; // using System.Threading.Tasks; -// using Convey.MessageBrokers; -// using Convey.MessageBrokers.Outbox; -// using Convey.MessageBrokers.RabbitMQ; +// using Paralax.MessageBrokers; +// using Paralax.MessageBrokers.Outbox; +// using Paralax.MessageBrokers.RabbitMQ; // using Microsoft.AspNetCore.Http; // using Microsoft.Extensions.Logging; // using OpenTracing; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/MiniSpace.Services.MediaFiles.Api.csproj b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/MiniSpace.Services.MediaFiles.Api.csproj index f5657baf4..f7ba851a4 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/MiniSpace.Services.MediaFiles.Api.csproj +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/MiniSpace.Services.MediaFiles.Api.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/Program.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/Program.cs index 914e85b0e..0879a39ec 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/Program.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Api/Program.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -17,8 +17,11 @@ using MiniSpace.Services.MediaFiles.Application.Services; using MiniSpace.Services.MediaFiles.Infrastructure; using DotNetEnv; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Text; +using Paralax.Types; +using Paralax.Core; + namespace MiniSpace.Services.MediaFiles.Api { @@ -30,7 +33,7 @@ public static async Task Main(string[] args) await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/CleanupUnassociatedFiles.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/CleanupUnassociatedFiles.cs index e97742e97..5423a1869 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/CleanupUnassociatedFiles.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/CleanupUnassociatedFiles.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.MediaFiles.Application.Commands { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/DeleteMediaFile.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/DeleteMediaFile.cs index 3148a64c2..a6a2f9d6d 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/DeleteMediaFile.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/DeleteMediaFile.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.MediaFiles.Application.Commands { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/CleanupUnassociatedFilesHandler.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/CleanupUnassociatedFilesHandler.cs index 25069f3e4..770090734 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/CleanupUnassociatedFilesHandler.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/CleanupUnassociatedFilesHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.MediaFiles.Application.Events; using MiniSpace.Services.MediaFiles.Application.Services; using MiniSpace.Services.MediaFiles.Core.Entities; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs index 119b249d9..a20a7424b 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/Handlers/DeleteMediaFileHandler.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.MediaFiles.Application.Events; using MiniSpace.Services.MediaFiles.Application.Exceptions; using MiniSpace.Services.MediaFiles.Application.Services; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs index 9cec78317..bae526a9e 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadFile.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.MediaFiles.Application.Commands diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadMediaFile.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadMediaFile.cs index 5cb2e94cc..6ae1e9216 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadMediaFile.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Commands/UploadMediaFile.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.MediaFiles.Application.Commands diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventFileUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventFileUploaded.cs index f373edd4b..4c6546ccd 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventFileUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventFileUploaded.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.MediaFiles.Application.Events diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventImageUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventImageUploaded.cs index 4ee482558..ebcabd3f5 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventImageUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/EventImageUploaded.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.MediaFiles.Application.Events diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStarted.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStarted.cs index 4781c5057..f1ec4ef9f 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStarted.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStarted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.MediaFiles.Application.Events { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStopped.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStopped.cs index 5bcd641fc..a049af326 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStopped.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/FileCleanupBackgroundWorkerStopped.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.MediaFiles.Application.Events { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/GeneralFileUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/GeneralFileUploaded.cs index babd65193..dbd2c44d4 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/GeneralFileUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/GeneralFileUploaded.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.MediaFiles.Application.Events diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs index 71e638828..1717469c4 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.MediaFiles.Application.Events diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileUploaded.cs index 5f2cf96ad..9e7d71efe 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/MediaFileUploaded.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.MediaFiles.Application.Events { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/OrganizationImageUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/OrganizationImageUploaded.cs index cd97af712..a24c6d6d0 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/OrganizationImageUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/OrganizationImageUploaded.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.MediaFiles.Application.Events diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs index 374b1b79b..0adaa5e64 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/PostFileUploaded.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.MediaFiles.Application.Events diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/StudentImageUploaded.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/StudentImageUploaded.cs index 1af63e94a..18264b7c7 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/StudentImageUploaded.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/StudentImageUploaded.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.MediaFiles.Application.Events diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/UnassociatedFilesCleaned.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/UnassociatedFilesCleaned.cs index 757821ca7..b5ae1973e 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/UnassociatedFilesCleaned.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Events/UnassociatedFilesCleaned.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.MediaFiles.Application.Events { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Extensions.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Extensions.cs index 6553ffbae..fbcc2e587 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Extensions.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Extensions.cs @@ -1,12 +1,12 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.MediaFiles.Application { public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/MiniSpace.Services.MediaFiles.Application.csproj b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/MiniSpace.Services.MediaFiles.Application.csproj index 4d25b0857..d8c6f51a3 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/MiniSpace.Services.MediaFiles.Application.csproj +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/MiniSpace.Services.MediaFiles.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetMediaFile.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetMediaFile.cs index 06ad3f327..306b9f377 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetMediaFile.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetMediaFile.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using Microsoft.AspNetCore.Mvc; using MiniSpace.Services.MediaFiles.Application.Dto; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetOriginalMediaFile.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetOriginalMediaFile.cs index 09c9fdf65..554b26a67 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetOriginalMediaFile.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Queries/GetOriginalMediaFile.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.MediaFiles.Application.Dto; namespace MiniSpace.Services.MediaFiles.Application.Queries diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IEventMapper.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IEventMapper.cs index b26bb7e2e..bc893432b 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.MediaFiles.Core.Events; namespace MiniSpace.Services.MediaFiles.Application.Services diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IMessageBroker.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IMessageBroker.cs index 2af117300..c2ab2e8ce 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.MediaFiles.Application.Services { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Contexts/AppContextFactory.cs index db1770db5..3adb9f335 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.MediaFiles.Application; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index cce8e588e..e5cf410b8 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.MediaFiles.Infrastructure.Decorators { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 2fe4847f6..2b85d5096 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.MediaFiles.Infrastructure.Decorators { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index da84e4638..489b6d7cb 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; namespace MiniSpace.Services.MediaFiles.Infrastructure.Exceptions { diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 48a0dab98..208bbd7a7 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.MediaFiles.Application.Exceptions; using MiniSpace.Services.MediaFiles.Core.Exceptions; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Extensions.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Extensions.cs index 805518fb6..cf13905c3 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -47,7 +47,7 @@ namespace MiniSpace.Services.MediaFiles.Infrastructure { public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddSingleton(); @@ -60,14 +60,7 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) builder.Services.AddTransient(ctx => ctx.GetRequiredService().Create()); builder.Services.TryDecorate(typeof(ICommandHandler<>), typeof(OutboxCommandHandlerDecorator<>)); builder.Services.TryDecorate(typeof(IEventHandler<>), typeof(OutboxEventHandlerDecorator<>)); - builder.Services.AddSingleton(serviceProvider => - { - var mongoDbOptions = serviceProvider.GetRequiredService(); - var mongoClient = new MongoClient(mongoDbOptions.ConnectionString); - var database = mongoClient.GetDatabase(mongoDbOptions.Database); - return new GridFSService(database); - }); - + var awsOptions = new AwsOptions { AccessKeyId = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"), @@ -110,7 +103,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/Extensions.cs index e358df03b..4bd1c1bb7 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/Extensions.cs @@ -1,5 +1,5 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; +using Paralax.CQRS.Logging; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.MediaFiles.Application.Commands; @@ -7,7 +7,7 @@ namespace MiniSpace.Services.MediaFiles.Infrastructure.Logging { internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(UploadMediaFile).Assembly; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 7621c7e11..5dfdaee99 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,4 +1,4 @@ -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using MiniSpace.Services.MediaFiles.Application.Commands; namespace MiniSpace.Services.MediaFiles.Infrastructure.Logging diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/MiniSpace.Services.MediaFiles.Infrastructure.csproj b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/MiniSpace.Services.MediaFiles.Infrastructure.csproj index e2325a2e4..167c09161 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/MiniSpace.Services.MediaFiles.Infrastructure.csproj +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/MiniSpace.Services.MediaFiles.Infrastructure.csproj @@ -8,32 +8,37 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs index 67022786e..a2a5bdc35 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Documents/FileSourceInfoDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.MediaFiles.Core.Entities; using MongoDB.Bson; using System; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetMediaFileHandler.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetMediaFileHandler.cs index 33b6734bc..2598d3344 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetMediaFileHandler.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetMediaFileHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.MediaFiles.Application.Dto; using MiniSpace.Services.MediaFiles.Application.Queries; using MiniSpace.Services.MediaFiles.Application.Services; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetOriginalMediaFileHandler.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetOriginalMediaFileHandler.cs index 8c7c6c61b..33bcd50f6 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetOriginalMediaFileHandler.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Queries/Handlers/GetOriginalMediaFileHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.MediaFiles.Application.Dto; using MiniSpace.Services.MediaFiles.Application.Queries; using MiniSpace.Services.MediaFiles.Application.Services; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Repositories/FileSourceInfoMongoRepository.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Repositories/FileSourceInfoMongoRepository.cs index c0b38a528..3e32f77fe 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Repositories/FileSourceInfoMongoRepository.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Mongo/Repositories/FileSourceInfoMongoRepository.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.MediaFiles.Core.Entities; using MiniSpace.Services.MediaFiles.Core.Repositories; using MiniSpace.Services.MediaFiles.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/EventMapper.cs index ff2fb0c3f..92bc256b8 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.MediaFiles.Application.Services; using MiniSpace.Services.MediaFiles.Core; using MiniSpace.Services.MediaFiles.Core.Events; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/GridFSService.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/GridFSService.cs deleted file mode 100644 index 35c7e60a4..000000000 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/GridFSService.cs +++ /dev/null @@ -1,35 +0,0 @@ -using MiniSpace.Services.MediaFiles.Application.Services; -using MongoDB.Bson; -using MongoDB.Driver; -using MongoDB.Driver.GridFS; - -namespace MiniSpace.Services.MediaFiles.Infrastructure.Services -{ - public class GridFSService: IGridFSService - { - private readonly IMongoDatabase _database; - private readonly GridFSBucket _gridFSBucket; - - public GridFSService(IMongoDatabase database) - { - _database = database; - _gridFSBucket = new GridFSBucket(_database); - } - - public async Task UploadFileAsync(string fileName, Stream fileStream) - { - ObjectId fileId = await _gridFSBucket.UploadFromStreamAsync(fileName, fileStream); - return fileId; - } - - public async Task DownloadFileAsync(ObjectId fileId, Stream destination) - { - await _gridFSBucket.DownloadToStreamAsync(fileId, destination); - } - - public async Task DeleteFileAsync(ObjectId fileId) - { - await _gridFSBucket.DeleteAsync(fileId); - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MessageBroker.cs index 5c35834ef..d792ea854 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/Workers/FileCleanupWorker.cs b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/Workers/FileCleanupWorker.cs index 243d9da1b..675c4195d 100644 --- a/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/Workers/FileCleanupWorker.cs +++ b/MiniSpace.Services.MediaFiles/src/MiniSpace.Services.MediaFiles.Infrastructure/Services/Workers/FileCleanupWorker.cs @@ -1,8 +1,8 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Commands; +using Paralax.Persistence.MongoDB; using Microsoft.Extensions.Hosting; using MiniSpace.Services.MediaFiles.Application.Commands; using MiniSpace.Services.MediaFiles.Application.Events; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/MiniSpace.Services.Notifications.Api.csproj b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/MiniSpace.Services.Notifications.Api.csproj index 16804845e..451f71040 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/MiniSpace.Services.Notifications.Api.csproj +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/MiniSpace.Services.Notifications.Api.csproj @@ -7,10 +7,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/Program.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/Program.cs index e7eb3260c..3e955e46e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/Program.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Api/Program.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.Builder; @@ -17,6 +17,9 @@ using MiniSpace.Services.Notifications.Application.Queries; using MiniSpace.Services.Notifications.Infrastructure; using MiniSpace.Services.Notifications.Application.Hubs; +using Paralax.Types; +using Paralax.Core; + namespace MiniSpace.Services.Notifications.Api @@ -27,7 +30,7 @@ public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => { - services.AddConvey() + services.AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure(); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/CreateNotification.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/CreateNotification.cs index 064040807..04f5e0718 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/CreateNotification.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/CreateNotification.cs @@ -1,4 +1,6 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; +using System; +using System.Collections.Generic; namespace MiniSpace.Services.Notifications.Application.Commands { @@ -7,15 +9,24 @@ public class CreateNotification : ICommand public Guid NotificationId { get; } public Guid UserId { get; } public string Message { get; } - public IEnumerable StudentIds { get; } - public Guid EventId { get; } - public CreateNotification(Guid notificationId, Guid userId, string message, IEnumerable studentIds, Guid eventId) + public IEnumerable UsersId { get; } + public Guid? EventId { get; } + public Guid? PostId { get; } + public Guid? OrganizationId { get; } + public string NotificationType { get; } + + public CreateNotification(Guid notificationId, Guid userId, string message, + IEnumerable userIds, string notificationType, + Guid? eventId = null, Guid? postId = null, Guid? organizationId = null) { NotificationId = notificationId; UserId = userId; Message = message; - StudentIds = studentIds; + UsersId = userIds; + NotificationType = notificationType; EventId = eventId; + PostId = postId; + OrganizationId = organizationId; } } } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/DeleteNotification.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/DeleteNotification.cs index f16b858b3..37039da93 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/DeleteNotification.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/DeleteNotification.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.AspNetCore.Mvc; namespace MiniSpace.Services.Notifications.Application.Commands diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/CreateNotificationHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/CreateNotificationHandler.cs index 4b38453a6..dc3b4d082 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/CreateNotificationHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/CreateNotificationHandler.cs @@ -1,79 +1,130 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Application.Services.Clients; -using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Events.External; using System; -using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; namespace MiniSpace.Services.Notifications.Application.Commands.Handlers { public class CreateNotificationHandler : ICommandHandler { - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IFriendsServiceClient _friendsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; + private readonly IPostsServiceClient _postsServiceClient; + private readonly IOrganizationsServiceClient _organizationsServiceClient; private readonly IMessageBroker _messageBroker; + private readonly IBaseUrlService _baseUrlService; public CreateNotificationHandler( - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IFriendsServiceClient friendsServiceClient, IEventsServiceClient eventsServiceClient, - IMessageBroker messageBroker - ) + IPostsServiceClient postsServiceClient, + IOrganizationsServiceClient organizationsServiceClient, + IMessageBroker messageBroker, + IBaseUrlService baseUrlService + ) { _studentNotificationsRepository = studentNotificationsRepository; _friendsServiceClient = friendsServiceClient; _eventsServiceClient = eventsServiceClient; + _postsServiceClient = postsServiceClient; + _organizationsServiceClient = organizationsServiceClient; _messageBroker = messageBroker; + _baseUrlService = baseUrlService; } public async Task HandleAsync(CreateNotification command, CancellationToken cancellationToken = default) { - var eventDetails = await _eventsServiceClient.GetEventAsync(command.EventId); - var eventLink = $"https://minispace.itsharppro.com/events/{eventDetails.Id}"; + string notificationMessage = string.Empty; + string detailedMessage = string.Empty; + string notificationLink = string.Empty; - foreach (var userId in command.StudentIds) + var baseUrl = _baseUrlService.GetBaseUrl(); + + switch (command.NotificationType.ToLower()) { - var notificationNotDetailed = $"

You have been invited to the event '{eventDetails.Name}' " + - $"Learn more: Click here.

"; - var notificationMessage = $"

You have been invited to the event '{eventDetails.Name}' organized by {eventDetails.Organizer.Name}. " + - $"This event will take place from {eventDetails.StartDate:yyyy-MM-dd} to {eventDetails.EndDate:yyyy-MM-dd} at {eventDetails.Location.Street}, {eventDetails.Location.City}. " + - $"The event has a capacity of {eventDetails.Capacity} and a registration fee of ${eventDetails.Fee}. " + - $"Learn more: Click here.

"; + case "eventinvitation": + if (command.EventId.HasValue) + { + var eventDetails = await _eventsServiceClient.GetEventAsync(command.EventId.Value); + notificationLink = $"{baseUrl}/events/{eventDetails.Id}"; + + notificationMessage = $"

You have been invited to the event '{eventDetails.Name}'. Click here to learn more.

"; + detailedMessage = $"

Event: {eventDetails.Name} organized by {eventDetails.Organizer.Name}. " + + $"The event will take place from {eventDetails.StartDate:yyyy-MM-dd} to {eventDetails.EndDate:yyyy-MM-dd} at {eventDetails.Location.Street}, {eventDetails.Location.City}. " + + $"Learn more: here.

"; + } + break; + + case "postcomment": + if (command.PostId.HasValue) + { + var postDetails = await _postsServiceClient.GetPostAsync(command.PostId.Value); + notificationLink = $"{baseUrl}/posts/{postDetails.Id}"; + + notificationMessage = $"

New comment on your post '{postDetails.TextContent}'. Click here to view.

"; + detailedMessage = $"

Your post '{postDetails.TextContent}' has received a new comment. " + + $"Learn more: here.

"; + } + break; + + case "organizationupdate": + if (command.OrganizationId.HasValue) + { + var orgDetails = await _organizationsServiceClient.GetOrganizationAsync(command.OrganizationId.Value); + notificationLink = $"{baseUrl}/organizations/{orgDetails.Id}"; + notificationMessage = $"

Update from your organization '{orgDetails.Name}'. Click here to read more.

"; + detailedMessage = $"

Your organization '{orgDetails.Name}' has shared a new update. " + + $"Learn more: here.

"; + } + break; + + case "generalnotification": + default: + notificationMessage = command.Message; + detailedMessage = command.Message; + break; + } + + // Send notifications to all users + foreach (var userId in command.UsersId) + { var notification = new Notification( command.NotificationId, userId, - notificationMessage, + detailedMessage, NotificationStatus.Unread, DateTime.UtcNow, null, NotificationEventType.NewEventInvitaion, - eventDetails.Id, - eventDetails?.Name + command.EventId ?? Guid.Empty, + null ); - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(userId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(userId); if (studentNotifications == null) { - studentNotifications = new StudentNotifications(userId); + studentNotifications = new UserNotifications(userId); } studentNotifications.AddNotification(notification); - await _studentNotificationsRepository.UpdateAsync(studentNotifications); var notificationCreatedEvent = new NotificationCreated( notificationId: notification.NotificationId, userId: userId, - message: notificationNotDetailed, + message: notificationMessage, createdAt: notification.CreatedAt, - eventType: notification.EventType.ToString(), - relatedEntityId: eventDetails.Id, - details: notificationMessage + eventType: command.NotificationType, + relatedEntityId: command.EventId ?? command.PostId ?? command.OrganizationId ?? Guid.Empty, + details: detailedMessage ); await _messageBroker.PublishAsync(notificationCreatedEvent); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/DeleteNotificationHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/DeleteNotificationHandler.cs index 051b777e4..46cafd0aa 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/DeleteNotificationHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/DeleteNotificationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Exceptions; using MiniSpace.Services.Notifications.Application.Services; @@ -10,11 +10,11 @@ namespace MiniSpace.Services.Notifications.Application.Commands.Handlers { public class DeleteNotificationHandler : ICommandHandler { - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IEventMapper _eventMapper; private readonly IMessageBroker _messageBroker; - public DeleteNotificationHandler(IStudentNotificationsRepository notificationRepository, IEventMapper eventMapper, IMessageBroker messageBroker) + public DeleteNotificationHandler(IUserNotificationsRepository notificationRepository, IEventMapper eventMapper, IMessageBroker messageBroker) { _studentNotificationsRepository = notificationRepository; _eventMapper = eventMapper; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/UpdateNotificationStatusHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/UpdateNotificationStatusHandler.cs index cb6dbad6d..58ba534ed 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/UpdateNotificationStatusHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/Handlers/UpdateNotificationStatusHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Exceptions; using MiniSpace.Services.Notifications.Core.Entities; @@ -10,9 +10,9 @@ namespace MiniSpace.Services.Notifications.Application.Commands.Handlers { public class UpdateNotificationStatusHandler : ICommandHandler { - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; - public UpdateNotificationStatusHandler(IStudentNotificationsRepository studentNotificationsRepository) + public UpdateNotificationStatusHandler(IUserNotificationsRepository studentNotificationsRepository) { _studentNotificationsRepository = studentNotificationsRepository; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/UpdateNotificationStatus.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/UpdateNotificationStatus.cs index 125fb89b7..60535c88a 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/UpdateNotificationStatus.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Commands/UpdateNotificationStatus.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.AspNetCore.Mvc; namespace MiniSpace.Services.Notifications.Application.Commands diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/AddressDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/AddressDto.cs index 6b08f4954..075e87e42 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/AddressDto.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/AddressDto.cs @@ -14,15 +14,5 @@ public class AddressDto public AddressDto() { } - - // public AddressDto(Address address) - // { - // BuildingName = address.BuildingName; - // Street = address.Street; - // BuildingNumber = address.BuildingNumber; - // ApartmentNumber = address.ApartmentNumber; - // City = address.City; - // ZipCode = address.ZipCode; - // } } } \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/CommentsDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Comments/CommentDto.cs similarity index 68% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/CommentsDto.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Comments/CommentDto.cs index 725559103..24a26b583 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/CommentsDto.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Comments/CommentDto.cs @@ -1,18 +1,16 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using MiniSpace.Services.Notifications.Core.Entities; +using MiniSpace.Services.Notifications.Application.Dto.Comments; -namespace MiniSpace.Services.Notifications.Application.Dto +namespace MiniSpace.Services.Notifications.Application.Dto.Comments { public class CommentDto { public Guid Id { get; set; } public Guid ContextId { get; set; } public string CommentContext { get; set; } - public Guid StudentId { get; set; } - public string StudentName { get; set; } + public Guid UserId { get; set; } public IEnumerable Likes { get; set; } public Guid ParentId { get; set; } public string TextContent { get; set; } @@ -21,5 +19,10 @@ public class CommentDto public DateTime LastReplyAt { get; set; } public int RepliesCount { get; set; } public bool IsDeleted { get; set; } + public IEnumerable Replies { get; set; } + + public CommentDto() + { + } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Comments/ReplyDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Comments/ReplyDto.cs new file mode 100644 index 000000000..003effa86 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Comments/ReplyDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Notifications.Application.Dto.Comments +{ + public class ReplyDto + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid CommentId { get; set; } + public string TextContent { get; set; } + public DateTime CreatedAt { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/EventDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/EventDto.cs deleted file mode 100644 index 6ae31df7f..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/EventDto.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using MiniSpace.Services.Notifications.Application.DTO; -using MiniSpace.Services.Notifications.Core.Entities; - -namespace MiniSpace.Services.Notifications.Application.Dto -{ - public class EventDto - { - public Guid Id { get; set; } - public string Name { get; set; } - public string Description { get; set; } - public OrganizerDto Organizer { get; set; } - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } - public AddressDto Location { get; set; } - public IEnumerable MediaFiles { get; set; } - public int InterestedStudents { get; set; } - public int SignedUpStudents { get; set; } - public int Capacity { get; set; } - public decimal Fee { get; set; } - public string Category { get; set; } - public string Status { get; set; } - public DateTime PublishDate { get; set; } - public DateTime UpdatedAt { get; set; } - public bool IsSignedUp { get; set; } - public bool IsInterested { get; set; } - public int? StudentRating { get; set; } - public IEnumerable FriendsInterestedIn { get; set; } - public IEnumerable FriendsSignedUp { get; set; } - - public EventDto() - { - } - - // public EventDto(Event @event, Guid studentId) - // { - // Id = @event.Id; - // Name = @event.Name; - // Description = @event.Description; - // Organizer = new OrganizerDto(@event.Organizer); - // StartDate = @event.StartDate; - // EndDate = @event.EndDate; - // // Location = new AddressDto(@event.Location); - // MediaFiles = @event.MediaFiles; - // InterestedStudents = @event.InterestedStudents.Count(); - // SignedUpStudents = @event.SignedUpStudents.Count(); - // Capacity = @event.Capacity; - // Fee = @event.Fee; - // Category = @event.Category.ToString(); - // Status = @event.State.ToString(); - // PublishDate = @event.PublishDate; - // IsSignedUp = @event.SignedUpStudents.Any(x => x.StudentId == studentId); - // IsInterested = @event.InterestedStudents.Any(x => x.StudentId == studentId); - // StudentRating = @event.Ratings.FirstOrDefault(x => x.StudentId == studentId)?.Value; - // FriendsInterestedIn = Enumerable.Empty(); - // FriendsSignedUp = Enumerable.Empty(); - // } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/EventDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/EventDto.cs new file mode 100644 index 000000000..9d669b36f --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/EventDto.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using MiniSpace.Services.Notifications.Core.Entities; + +namespace MiniSpace.Services.Notifications.Application.Dto.Events +{ + public class EventDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public OrganizerDto Organizer { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public AddressDto Location { get; set; } + public IList MediaFilesUrl { get; set; } + public string BannerUrl { get; set; } + public int InterestedStudents { get; set; } + public int SignedUpStudents { get; set; } + public int Capacity { get; set; } + public decimal Fee { get; set; } + public string Category { get; set; } + public string State { get; set; } + public DateTime PublishDate { get; set; } + public DateTime UpdatedAt { get; set; } + public bool IsSignedUp { get; set; } + public bool IsInterested { get; set; } + public int? StudentRating { get; set; } + public IEnumerable FriendsInterestedIn { get; set; } + public IEnumerable FriendsSignedUp { get; set; } + public string Visibility { get; set; } + public EventSettingsDto Settings { get; set; } + + public EventDto() + { + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/EventSettingsDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/EventSettingsDto.cs new file mode 100644 index 000000000..219984d91 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/EventSettingsDto.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Notifications.Application.Dto.Events +{ + public class EventSettingsDto + { + public bool RequiresApproval { get; set; } + public bool IsOnlineEvent { get; set; } + public bool IsPrivate { get; set; } + public bool RequiresRSVP { get; set; } + public bool AllowsGuests { get; set; } + public bool ShowAttendeesPublicly { get; set; } + public bool SendReminders { get; set; } + public int ReminderDaysBefore { get; set; } + public bool EnableChat { get; set; } + public bool AllowComments { get; set; } + public bool RequiresPayment { get; set; } + public string PaymentMethod { get; set; } + public string PaymentReceiverDetails { get; set; } + public string PaymentGateway { get; set; } + public bool IssueTickets { get; set; } + public int MaxTicketsPerPerson { get; set; } + public decimal TicketPrice { get; set; } + public bool RecordEvent { get; set; } + public string CustomTermsAndConditions { get; set; } + public IDictionary CustomFields { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/OrganizerDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/OrganizerDto.cs similarity index 91% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/OrganizerDto.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/OrganizerDto.cs index 9a7a026be..481203816 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/OrganizerDto.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Events/OrganizerDto.cs @@ -1,7 +1,7 @@ using System; using MiniSpace.Services.Notifications.Core.Entities; -namespace MiniSpace.Services.Notifications.Application.DTO +namespace MiniSpace.Services.Notifications.Application.Dto.Events { public class OrganizerDto { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/FriendDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/FriendDto.cs index 94f1dbdf3..a686d0b41 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/FriendDto.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/FriendDto.cs @@ -8,7 +8,7 @@ namespace MiniSpace.Services.Notifications.Application.Dto public class FriendDto { public Guid Id { get; set; } - public Guid StudentId { get; set; } + public Guid UserId { get; set; } public Guid FriendId { get; set; } public DateTime CreatedAt { get; set; } public string FriendState { get; set; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationDto.cs new file mode 100644 index 000000000..f2c6aa985 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationDto.cs @@ -0,0 +1,31 @@ +using MiniSpace.Services.Notifications.Core.Entities; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace MiniSpace.Services.Notifications.Application.Dto.Organizations +{ + [ExcludeFromCodeCoverage] + public class OrganizationDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + public Guid OwnerId { get; set; } + public string DefaultRoleName { get; set; } + + public string Address { get; set; } + public string Country { get; set; } + public string City { get; set; } + public string Telephone { get; set; } + public string Email { get; set; } + + public IEnumerable Users { get; set; } + public OrganizationSettingsDto Settings { get; set; } + + public OrganizationDto() + { + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationRoleDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationRoleDto.cs new file mode 100644 index 000000000..569053243 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationRoleDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Notifications.Application.Dto.Organizations +{ + [ExcludeFromCodeCoverage] + public class OrganizationRoleDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public Dictionary Permissions { get; set; } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationSettingsDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationSettingsDto.cs new file mode 100644 index 000000000..81ef31f89 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationSettingsDto.cs @@ -0,0 +1,25 @@ + +namespace MiniSpace.Services.Notifications.Application.Dto.Organizations +{ + public class OrganizationSettingsDto + { + public bool IsVisible { get; set; } + public bool IsPublic { get; set; } + public bool IsPrivate { get; set; } + public bool CanAddComments { get; set; } + public bool CanAddReactions { get; set; } + public bool CanPostPosts { get; set; } + public bool CanPostEvents { get; set; } + public bool CanMakeReposts { get; set; } + public bool CanAddCommentsToPosts { get; set; } + public bool CanAddReactionsToPosts { get; set; } + public bool CanAddCommentsToEvents { get; set; } + public bool CanAddReactionsToEvents { get; set; } + public bool DisplayFeedInMainOrganization { get; set; } + + public OrganizationSettingsDto() + { + + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationUserDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationUserDto.cs new file mode 100644 index 000000000..3f0c7cc3f --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationUserDto.cs @@ -0,0 +1,17 @@ +using System; + +using System.Diagnostics.CodeAnalysis; + +namespace MiniSpace.Services.Notifications.Application.Dto.Organizations +{ + [ExcludeFromCodeCoverage] + public class OrganizationUserDto + { + public Guid Id { get; set; } + public OrganizationRoleDto Role { get; set; } + + public OrganizationUserDto() + { + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationUsersDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationUsersDto.cs new file mode 100644 index 000000000..e43574a3a --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Organizations/OrganizationUsersDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Notifications.Application.Dto.Organizations +{ + public class OrganizationUsersDto + { + public OrganizationDto Organization { get; set; } + public IEnumerable Users { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/ParticipantDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/ParticipantDto.cs index 777448455..4043543cd 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/ParticipantDto.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/ParticipantDto.cs @@ -4,7 +4,6 @@ namespace MiniSpace.Services.Notifications.Application.Dto { public class ParticipantDto { - public Guid StudentId { get; set; } - public string Name { get; set; } + public Guid UserId { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/PostDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/PostDto.cs deleted file mode 100644 index 8b94260ec..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/PostDto.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace MiniSpace.Services.Notifications.Application.Dto -{ - public class PostDto - { - public Guid Id { get; set; } - public Guid EventId { get; set; } - public Guid OrganizerId { get; set; } - public string TextContent { get; set; } - public string MediaContent { get; set; } - public string State { get; set; } - public DateTime? PublishDate { get; set; } - public DateTime CreatedAt { get; set; } - public DateTime? UpdatedAt { get; set; } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Posts/PostDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Posts/PostDto.cs new file mode 100644 index 000000000..7e0e9e95a --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/Posts/PostDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Notifications.Application.Dto.Posts +{ + public class PostDto + { + public Guid Id { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public string TextContent { get; set; } + public IEnumerable MediaFiles { get; set; } + public string State { get; set; } + public DateTime? PublishDate { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public string Context { get; set; } + public string Visibility { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/StudentDto.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/StudentDto.cs index 43a11904f..baa82eb38 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/StudentDto.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Dto/StudentDto.cs @@ -3,7 +3,7 @@ namespace MiniSpace.Services.Notifications.Application.Dto { - public class StudentDto + public class UserDto { public Guid Id { get; set; } public string Email { get; set; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/CommentCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/CommentCreated.cs deleted file mode 100644 index d6acb360e..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/CommentCreated.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using System; - -namespace MiniSpace.Services.Notifications.Application.Events.External -{ - [Message("comments")] - public class CommentCreated : IEvent - { - public Guid CommentId { get; } - - public CommentCreated(Guid commentId) - { - CommentId = commentId; - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/CommentUpdated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/CommentUpdated.cs deleted file mode 100644 index e691425ab..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/CommentUpdated.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using System; - -namespace MiniSpace.Services.Notifications.Application.Events.External -{ - [Message("comments")] - public class CommentUpdated : IEvent - { - public Guid CommentId { get; } - - public CommentUpdated(Guid commentId) - { - CommentId = commentId; - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/CommentCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/CommentCreated.cs new file mode 100644 index 000000000..fb4569e3a --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/CommentCreated.cs @@ -0,0 +1,42 @@ +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using System; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Comments +{ + [Message("comments")] + public class CommentCreated : IEvent + { + public Guid CommentId { get; } + public Guid ContextId { get; } + public string CommentContext { get; } + public Guid UserId { get; } + public Guid ParentId { get; } + public string TextContent { get; } + public DateTime CreatedAt { get; } + public DateTime LastUpdatedAt { get; } + public int RepliesCount { get; } + public bool IsDeleted { get; } + public string UserName { get; } + public string ProfileImageUrl { get; } + + public CommentCreated(Guid commentId, Guid contextId, string commentContext, Guid userId, + Guid parentId, string textContent, DateTime createdAt, + DateTime lastUpdatedAt, int repliesCount, bool isDeleted, + string userName, string profileImageUrl) + { + CommentId = commentId; + ContextId = contextId; + CommentContext = commentContext; + UserId = userId; + ParentId = parentId; + TextContent = textContent; + CreatedAt = createdAt; + LastUpdatedAt = lastUpdatedAt; + RepliesCount = repliesCount; + IsDeleted = isDeleted; + UserName = userName; + ProfileImageUrl = profileImageUrl; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/CommentUpdated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/CommentUpdated.cs new file mode 100644 index 000000000..851e1a1a9 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/CommentUpdated.cs @@ -0,0 +1,30 @@ +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using System; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Comments +{ + [Message("comments")] + public class CommentUpdated : IEvent + { + public Guid CommentId { get; } + public Guid UserId { get; } + public string CommentContext { get; } + public DateTime UpdatedAt { get; } + public string CommentContent { get; } + public string UserName { get; } + public string ProfileImageUrl { get; } + + public CommentUpdated(Guid commentId, Guid userId, string commentContext, + DateTime updatedAt, string commentContent, string userName, string profileImageUrl) + { + CommentId = commentId; + UserId = userId; + CommentContext = commentContext; + UpdatedAt = updatedAt; + CommentContent = commentContent; + UserName = userName; + ProfileImageUrl = profileImageUrl; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/CommentCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/CommentCreatedHandler.cs new file mode 100644 index 000000000..ce1e2b934 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/CommentCreatedHandler.cs @@ -0,0 +1,138 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using MiniSpace.Services.Notifications.Core.Repositories; +using MiniSpace.Services.Notifications.Application.Services; +using MiniSpace.Services.Notifications.Application.Services.Clients; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Notifications.Application.Hubs; +using MiniSpace.Services.Notifications.Application.Dto; +using Microsoft.AspNetCore.SignalR; +using MiniSpace.Services.Notifications.Core.Entities; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Comments.Handlers +{ + public class CommentCreatedHandler : IEventHandler + { + private readonly ICommentsServiceClient _commentsServiceClient; + private readonly IEventsServiceClient _eventsServiceClient; + private readonly IPostsServiceClient _postsServiceClient; + private readonly IOrganizationsServiceClient _organizationsServiceClient; + private readonly IUserNotificationsRepository _userNotificationsRepository; + private readonly ILogger _logger; + private readonly IHubContext _hubContext; + + public CommentCreatedHandler( + ICommentsServiceClient commentsServiceClient, + IEventsServiceClient eventsServiceClient, + IPostsServiceClient postsServiceClient, + IOrganizationsServiceClient organizationsServiceClient, + IUserNotificationsRepository userNotificationsRepository, + ILogger logger, + IHubContext hubContext) + { + _commentsServiceClient = commentsServiceClient; + _eventsServiceClient = eventsServiceClient; + _postsServiceClient = postsServiceClient; + _organizationsServiceClient = organizationsServiceClient; + _userNotificationsRepository = userNotificationsRepository; + _logger = logger; + _hubContext = hubContext; + } + + public async Task HandleAsync(CommentCreated @event, CancellationToken cancellationToken = default) + { + try + { + var entityOwnerId = Guid.Empty; + string entityName = string.Empty; + + if (@event.CommentContext.Equals("OrganizationEvent", StringComparison.OrdinalIgnoreCase)) + { + var organizationEvent = await _eventsServiceClient.GetEventAsync(@event.ContextId); + if (organizationEvent != null) + { + entityOwnerId = organizationEvent.Organizer.OrganizationId != Guid.Empty + ? organizationEvent.Organizer.OrganizationId + : organizationEvent.Organizer.Id; + entityName = organizationEvent.Name; + } + } + else if (@event.CommentContext.Equals("OrganizationPost", StringComparison.OrdinalIgnoreCase)) + { + var organizationPost = await _postsServiceClient.GetPostAsync(@event.ContextId); + if (organizationPost != null) + { + entityOwnerId = organizationPost.UserId.HasValue ? organizationPost.UserId.Value : Guid.Empty; + entityName = organizationPost.TextContent; + } + } + else if (@event.CommentContext.Equals("UserEvent", StringComparison.OrdinalIgnoreCase)) + { + var userEvent = await _eventsServiceClient.GetEventAsync(@event.ContextId); + if (userEvent != null) + { + entityOwnerId = userEvent.Organizer.Id; + entityName = userEvent.Name; + } + } + else if (@event.CommentContext.Equals("UserPost", StringComparison.OrdinalIgnoreCase)) + { + var userPost = await _postsServiceClient.GetPostAsync(@event.ContextId); + if (userPost != null) + { + entityOwnerId = userPost.UserId.HasValue ? userPost.UserId.Value : Guid.Empty; + entityName = userPost.TextContent; + } + } + else + { + _logger.LogError($"Unknown CommentContext: {@event.CommentContext}"); + return; + } + + if (entityOwnerId == Guid.Empty) + { + _logger.LogError("Entity owner not found for the context of the comment."); + return; + } + + var notificationMessage = $"A new comment has been made on '{entityName}' by {@event.UserName}. Profile Image"; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: entityOwnerId, + message: notificationMessage, + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: @event.CommentId, + eventType: NotificationEventType.CommentCreated + ); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(entityOwnerId) + ?? new UserNotifications(entityOwnerId); + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + var notificationDto = new NotificationDto + { + UserId = entityOwnerId, + Message = notificationMessage, + CreatedAt = notification.CreatedAt, + EventType = NotificationEventType.CommentCreated, + RelatedEntityId = @event.CommentId, + Details = $"

{@event.UserName} commented: '{@event.TextContent}' on '{entityName}'.

Profile Image" + }; + + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + } + catch (Exception ex) + { + _logger.LogError($"Failed to handle CommentCreated event: {ex.Message}"); + } + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/CommentUpdatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/CommentUpdatedHandler.cs similarity index 78% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/CommentUpdatedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/CommentUpdatedHandler.cs index 7234a9295..8a53ce69e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/CommentUpdatedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/CommentUpdatedHandler.cs @@ -1,24 +1,27 @@ using System; -using Convey.CQRS.Events; +using System.Threading; +using System.Threading.Tasks; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; -using System.Threading.Tasks; -using System.Threading; using MiniSpace.Services.Notifications.Application.Services.Clients; using Microsoft.Extensions.Logging; using Microsoft.AspNetCore.SignalR; using MiniSpace.Services.Notifications.Application.Hubs; using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Events.External.Comments; +using MiniSpace.Services.Notifications.Application.Dto.Events; +using MiniSpace.Services.Notifications.Application.Dto.Comments; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Comments.Handlers { public class CommentUpdatedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly ICommentsServiceClient _commentsServiceClient; private readonly ILogger _logger; private readonly IHubContext _hubContext; @@ -27,7 +30,7 @@ public CommentUpdatedHandler( IMessageBroker messageBroker, IStudentsServiceClient studentsServiceClient, IEventsServiceClient eventsServiceClient, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, ICommentsServiceClient commentsServiceClient, ILogger logger, IHubContext hubContext) @@ -75,19 +78,20 @@ public async Task HandleAsync(CommentUpdated eventArgs, CancellationToken cancel throw; } - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(commentDetails.StudentId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(commentDetails.UserId); if (studentNotifications == null) { - studentNotifications = new StudentNotifications(commentDetails.StudentId); + studentNotifications = new UserNotifications(commentDetails.UserId); } + // Create notification for the user who updated the comment var userNotification = new Notification( notificationId: Guid.NewGuid(), - userId: commentDetails.StudentId, + userId: commentDetails.UserId, message: $"Your updated comment on the event '{eventDetails.Name}' has been processed.", status: NotificationStatus.Unread, createdAt: DateTime.UtcNow, - updatedAt: DateTime.UtcNow, + updatedAt: DateTime.UtcNow, relatedEntityId: eventArgs.CommentId, eventType: NotificationEventType.CommentUpdated ); @@ -109,9 +113,10 @@ public async Task HandleAsync(CommentUpdated eventArgs, CancellationToken cancel await _messageBroker.PublishAsync(notificationUpdatedEvent); + // Broadcast the updated notification via SignalR var notificationDto = new NotificationDto { - UserId = commentDetails.StudentId, + UserId = commentDetails.UserId, Message = userNotification.Message, CreatedAt = userNotification.CreatedAt, EventType = NotificationEventType.CommentUpdated, @@ -120,21 +125,23 @@ public async Task HandleAsync(CommentUpdated eventArgs, CancellationToken cancel }; await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation("Broadcasted SignalR notification to all users."); + _logger.LogInformation("Broadcasted SignalR notification to the user."); - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventDetails.Organizer.Id); + // Organizer notification + var organizerNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventDetails.Organizer.Id); if (organizerNotifications == null) { - organizerNotifications = new StudentNotifications(eventDetails.Organizer.Id); + organizerNotifications = new UserNotifications(eventDetails.Organizer.Id); } + // Create notification for the organizer var organizerNotification = new Notification( notificationId: Guid.NewGuid(), userId: eventDetails.Organizer.Id, - message: $"{commentDetails.StudentName} has updated their comment on your event '{eventDetails.Name}'.", + message: $"{eventArgs.UserName} has updated their comment on your event '{eventDetails.Name}'.", status: NotificationStatus.Unread, createdAt: DateTime.UtcNow, - updatedAt: DateTime.UtcNow, + updatedAt: DateTime.UtcNow, relatedEntityId: eventArgs.CommentId, eventType: NotificationEventType.CommentUpdated ); @@ -142,12 +149,14 @@ public async Task HandleAsync(CommentUpdated eventArgs, CancellationToken cancel organizerNotifications.AddNotification(organizerNotification); await _studentNotificationsRepository.AddOrUpdateAsync(organizerNotifications); - var organizerNotificationDetailsHtml = $"

{commentDetails.StudentName} updated their comment on your event '{eventDetails.Name}': {commentDetails.CommentContext}

"; + // Prepare and send organizer notification details HTML, including profile image + var organizerNotificationDetailsHtml = $"

{eventArgs.UserName} updated their comment on your event '{eventDetails.Name}': {eventArgs.CommentContent}.

" + + $"Profile Image"; var organizerNotificationUpdatedEvent = new NotificationCreated( notificationId: Guid.NewGuid(), userId: eventDetails.Organizer.Id, - message: $"{commentDetails.StudentName} has updated their comment on your event '{eventDetails.Name}'.", + message: $"{eventArgs.UserName} has updated their comment on your event '{eventDetails.Name}'.", createdAt: DateTime.UtcNow, eventType: NotificationEventType.CommentUpdated.ToString(), relatedEntityId: eventArgs.CommentId, @@ -167,7 +176,7 @@ public async Task HandleAsync(CommentUpdated eventArgs, CancellationToken cancel }; await NotificationHub.BroadcastNotification(_hubContext, organizerNotificationDto, _logger); - _logger.LogInformation("Broadcasted SignalR notification to all users."); + _logger.LogInformation("Broadcasted SignalR notification to the event organizer."); } } } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/LikeAddedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/LikeAddedHandler.cs new file mode 100644 index 000000000..f725ff74a --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/Handlers/LikeAddedHandler.cs @@ -0,0 +1,89 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using MiniSpace.Services.Notifications.Core.Repositories; +using MiniSpace.Services.Notifications.Application.Services.Clients; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Notifications.Application.Hubs; +using MiniSpace.Services.Notifications.Application.Dto; +using Microsoft.AspNetCore.SignalR; +using MiniSpace.Services.Notifications.Core.Entities; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Comments.Handlers +{ + public class LikeAddedHandler : IEventHandler + { + private readonly ICommentsServiceClient _commentsServiceClient; + private readonly IPostsServiceClient _postsServiceClient; + private readonly IUserNotificationsRepository _userNotificationsRepository; + private readonly ILogger _logger; + private readonly IHubContext _hubContext; + + public LikeAddedHandler( + ICommentsServiceClient commentsServiceClient, + IPostsServiceClient postsServiceClient, + IUserNotificationsRepository userNotificationsRepository, + ILogger logger, + IHubContext hubContext) + { + _commentsServiceClient = commentsServiceClient; + _postsServiceClient = postsServiceClient; + _userNotificationsRepository = userNotificationsRepository; + _logger = logger; + _hubContext = hubContext; + } + + public async Task HandleAsync(LikeAdded @event, CancellationToken cancellationToken = default) + { + try + { + var commentDetails = await _commentsServiceClient.GetCommentAsync(@event.CommentId); + if (commentDetails == null) + { + _logger.LogError($"No comment details found for LikeAdded event. CommentId: {@event.CommentId}"); + return; + } + + var entityOwnerId = commentDetails.UserId; + var entityName = commentDetails.TextContent; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: entityOwnerId, + message: $"{@event.UserName} liked your comment '{entityName}'.", + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: @event.CommentId, + eventType: NotificationEventType.CommentLikeAdded + ); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(entityOwnerId) + ?? new UserNotifications(entityOwnerId); + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + var notificationDetails = $"

{@event.UserName} liked your comment: '{@event.CommentContext}'.

" + + $"Profile Image"; + + var notificationDto = new NotificationDto + { + UserId = entityOwnerId, + Message = notification.Message, + CreatedAt = notification.CreatedAt, + EventType = NotificationEventType.CommentLikeAdded, + RelatedEntityId = @event.CommentId, + Details = notificationDetails + }; + + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + } + catch (Exception ex) + { + _logger.LogError($"Failed to handle LikeAdded event: {ex.Message}"); + } + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/LikeAdded.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/LikeAdded.cs new file mode 100644 index 000000000..e63ba8184 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Comments/LikeAdded.cs @@ -0,0 +1,28 @@ +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using System; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Comments +{ + [Message("comments")] + public class LikeAdded : IEvent + { + public Guid CommentId { get; } + public Guid UserId { get; } + public string CommentContext { get; } + public DateTime LikedAt { get; } + public string UserName { get; } + public string ProfileImageUrl { get; } + + public LikeAdded(Guid commentId, Guid userId, string commentContext, + DateTime likedAt, string userName, string profileImageUrl) + { + CommentId = commentId; + UserId = userId; + CommentContext = commentContext; + LikedAt = likedAt; + UserName = userName; + ProfileImageUrl = profileImageUrl; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventCreated.cs deleted file mode 100644 index 45e6de27d..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventCreated.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using Convey.CQRS.Events; -using Convey.MessageBrokers; - -namespace MiniSpace.Services.Notifications.Application.Events.External -{ - [Message("events")] - public class EventCreated(Guid eventId, Guid organizerId, IEnumerable mediaFilesIds) : IEvent - { - public Guid EventId { get; set; } = eventId; - public Guid OrganizerId { get; set; } = organizerId; - public IEnumerable MediaFilesIds { get; set; } = mediaFilesIds; - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventDeleted.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventDeleted.cs deleted file mode 100644 index 9c00e2321..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventDeleted.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using Convey.CQRS.Events; -using Convey.MessageBrokers; - -namespace MiniSpace.Services.Notifications.Application.Events.External -{ - [Message("events")] - public class EventDeleted(Guid eventId) : IEvent - { - public Guid EventId { get; set; } = eventId; - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventCreated.cs new file mode 100644 index 000000000..444f7e7a2 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventCreated.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Events +{ + [Message("events")] + public class EventCreated : IEvent + { + public Guid EventId { get; set; } + /* OrganizerType := { User, Organization} */ + public string OrganizerType { get; set; } + public Guid OrganizerId { get; set; } + public IEnumerable MediaFilesUrls { get; set; } + + public EventCreated(Guid eventId, string organizerType, Guid organizerId, IEnumerable mediaFilesUrls) + { + EventId = eventId; + OrganizerType = organizerType; + OrganizerId = organizerId; + MediaFilesUrls = mediaFilesUrls; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventDeleted.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventDeleted.cs new file mode 100644 index 000000000..18e0d8b99 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventDeleted.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Events +{ + [Message("events")] + public class EventDeleted : IEvent + { + public Guid EventId { get; } + public string EventName { get; } + public Guid OrganizerId { get; } + public string OrganizerType { get; } + public DateTime StartDate { get; } + public DateTime EndDate { get; } + + public EventDeleted(Guid eventId, string eventName, Guid organizerId, + string organizerType, DateTime startDate, DateTime endDate) + { + EventId = eventId; + EventName = eventName; + OrganizerId = organizerId; + OrganizerType = organizerType; + StartDate = startDate; + EndDate = endDate; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventParticipantAdded.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventParticipantAdded.cs similarity index 88% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventParticipantAdded.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventParticipantAdded.cs index 8b2308806..8a2e68b2d 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventParticipantAdded.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventParticipantAdded.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Events { [Message("events")] public class EventParticipantAdded: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventParticipantRemoved.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventParticipantRemoved.cs similarity index 86% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventParticipantRemoved.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventParticipantRemoved.cs index e206982f9..1a5afa8b7 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventParticipantRemoved.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventParticipantRemoved.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Events { [Message("events")] public class EventParticipantRemoved: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventUpdated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventUpdated.cs similarity index 81% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventUpdated.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventUpdated.cs index a38251bab..58156e7b5 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EventUpdated.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/EventUpdated.cs @@ -1,11 +1,10 @@ using System; using System.Collections; using System.Collections.Generic; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; -namespace MiniSpace.Services.Notifications.Application.Events +namespace MiniSpace.Services.Notifications.Application.Events.Events { - [Contract] public class EventUpdated(Guid eventId, DateTime updatedAt, Guid updatedBy, IEnumerable mediaFilesIds) : IEvent { public Guid EventId { get; set; } = eventId; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventCreatedHandler.cs new file mode 100644 index 000000000..507f6c6c2 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventCreatedHandler.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using MiniSpace.Services.Notifications.Core.Repositories; +using MiniSpace.Services.Notifications.Application.Services; +using MiniSpace.Services.Notifications.Core.Entities; +using System.Text.Json; +using MiniSpace.Services.Notifications.Application.Services.Clients; +using MiniSpace.Services.Notifications.Application.Dto; +using Microsoft.Extensions.Logging; +using Microsoft.AspNetCore.SignalR; +using MiniSpace.Services.Notifications.Application.Hubs; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers +{ + public class EventCreatedHandler : IEventHandler + { + private readonly IMessageBroker _messageBroker; + private readonly IStudentsServiceClient _studentsServiceClient; + private readonly IFriendsServiceClient _friendsServiceClient; + private readonly IOrganizationsServiceClient _organizationsServiceClient; + private readonly IUserNotificationsRepository _userNotificationsRepository; + private readonly ILogger _logger; + private readonly IHubContext _hubContext; + + public EventCreatedHandler( + IMessageBroker messageBroker, + IStudentsServiceClient studentsServiceClient, + IFriendsServiceClient friendsServiceClient, + IOrganizationsServiceClient organizationsServiceClient, + IUserNotificationsRepository userNotificationsRepository, + ILogger logger, + IHubContext hubContext) + { + _messageBroker = messageBroker; + _studentsServiceClient = studentsServiceClient; + _friendsServiceClient = friendsServiceClient; + _organizationsServiceClient = organizationsServiceClient; + _userNotificationsRepository = userNotificationsRepository; + _logger = logger; + _hubContext = hubContext; + } + + public async Task HandleAsync(EventCreated eventCreated, CancellationToken cancellationToken) + { + try + { + // Check if the event is organized by an organization or a user + if (eventCreated.OrganizerType.Equals("Organization", StringComparison.OrdinalIgnoreCase)) + { + // Notify all members of the organization + await NotifyOrganizationMembersAsync(eventCreated); + } + else if (eventCreated.OrganizerType.Equals("User", StringComparison.OrdinalIgnoreCase)) + { + // Notify user's friends and followers + await NotifyUserFriendsAndFollowersAsync(eventCreated); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to handle EventCreated event: {ex.Message}"); + } + } + + private async Task NotifyOrganizationMembersAsync(EventCreated eventCreated) + { + try + { + var organizationUsers = await _organizationsServiceClient.GetOrganizationMembersAsync(eventCreated.OrganizerId); + + if (organizationUsers != null && organizationUsers.Users != null) + { + foreach (var user in organizationUsers.Users) + { + var notificationMessage = $"A new event has been created in your organization (Event ID: {eventCreated.EventId})."; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: user.Id, // Corrected from 'Members' to 'Users' + message: notificationMessage, + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: eventCreated.EventId, + eventType: NotificationEventType.NewEvent + ); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(user.Id) + ?? new UserNotifications(user.Id); + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + // Broadcast the notification to the user using SignalR + var notificationDto = new NotificationDto + { + UserId = user.Id, + Message = notificationMessage, + CreatedAt = notification.CreatedAt, + EventType = NotificationEventType.NewEvent, + RelatedEntityId = eventCreated.EventId, + Details = $"

A new event has been created in your organization.

" + }; + + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + } + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify organization members: {ex.Message}"); + } + } + + private async Task NotifyUserFriendsAndFollowersAsync(EventCreated eventCreated) + { + try + { + // Get the user's friends + var friends = await _friendsServiceClient.GetFriendsAsync(eventCreated.OrganizerId); + var friendIds = friends.Select(f => f.FriendId).ToList(); + + // Get the users who have requested friendship (followers) + var followers = await _friendsServiceClient.GetRequestsAsync(eventCreated.OrganizerId); + var followerIds = followers.Select(f => f.InviterId).ToList(); // Corrected from 'RequesterId' to 'InviterId' + + // Combine friends and followers into one list of user IDs to notify + var userIdsToNotify = friendIds.Concat(followerIds).Distinct().ToList(); + + foreach (var userId in userIdsToNotify) + { + var notificationMessage = $"A new event has been created by your friend or someone you follow (Event ID: {eventCreated.EventId})."; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: userId, + message: notificationMessage, + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: eventCreated.EventId, + eventType: NotificationEventType.NewEvent + ); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(userId) + ?? new UserNotifications(userId); + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + // Broadcast the notification to the user using SignalR + var notificationDto = new NotificationDto + { + UserId = userId, + Message = notificationMessage, + CreatedAt = notification.CreatedAt, + EventType = NotificationEventType.NewEvent, + RelatedEntityId = eventCreated.EventId, + Details = $"

A new event has been created by a user you follow or are friends with.

" + }; + + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify user's friends and followers: {ex.Message}"); + } + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventDeletedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventDeletedHandler.cs new file mode 100644 index 000000000..7a4e93ea8 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventDeletedHandler.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Hubs; +using MiniSpace.Services.Notifications.Application.Services.Clients; +using MiniSpace.Services.Notifications.Core.Entities; +using MiniSpace.Services.Notifications.Core.Repositories; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers +{ + public class EventDeletedHandler : IEventHandler + { + private readonly IEventsServiceClient _eventsServiceClient; + private readonly IUserNotificationsRepository _userNotificationsRepository; + private readonly ILogger _logger; + private readonly IHubContext _hubContext; + + public EventDeletedHandler( + IEventsServiceClient eventsServiceClient, + IUserNotificationsRepository userNotificationsRepository, + ILogger logger, + IHubContext hubContext) + { + _eventsServiceClient = eventsServiceClient; + _userNotificationsRepository = userNotificationsRepository; + _logger = logger; + _hubContext = hubContext; + } + + public async Task HandleAsync(EventDeleted eventDeleted, CancellationToken cancellationToken) + { + try + { + // Fetch participants and interested users for the event + var eventParticipants = await _eventsServiceClient.GetParticipantsAsync(eventDeleted.EventId); + + if (eventParticipants == null) + { + _logger.LogWarning($"No participants found for event {eventDeleted.EventId}"); + return; + } + + // Combine signed-up users and interested users + var allUsersToNotify = eventParticipants.SignedUpStudents + .Concat(eventParticipants.InterestedStudents) + .Distinct() + .Select(p => p.UserId) + .ToList(); + + // Notify all relevant users + foreach (var userId in allUsersToNotify) + { + var notificationMessage = $"The event '{eventDeleted.EventName}' (Event ID: {eventDeleted.EventId}) scheduled from {eventDeleted.StartDate:yyyy-MM-dd} to {eventDeleted.EndDate:yyyy-MM-dd} has been cancelled."; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: userId, + message: notificationMessage, + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: eventDeleted.EventId, + eventType: NotificationEventType.EventDeleted + ); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(userId) + ?? new UserNotifications(userId); + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + // Broadcast the notification to the user using SignalR + var notificationDto = new NotificationDto + { + UserId = userId, + Message = notificationMessage, + CreatedAt = notification.CreatedAt, + EventType = NotificationEventType.EventDeleted, + RelatedEntityId = eventDeleted.EventId, + Details = $"

The event '{eventDeleted.EventName}' has been cancelled.

" + }; + + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to handle EventDeleted event: {ex.Message}"); + } + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventParticipantAddedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventParticipantAddedHandler.cs similarity index 92% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventParticipantAddedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventParticipantAddedHandler.cs index 820a3efba..594cb6cf4 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventParticipantAddedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventParticipantAddedHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,12 +11,12 @@ using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Core.Repositories; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers { public class EventParticipantAddedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; private readonly ILogger _logger; @@ -24,7 +24,7 @@ public class EventParticipantAddedHandler : IEventHandler public EventParticipantAddedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, IEventsServiceClient eventsServiceClient, ILogger logger, @@ -40,11 +40,11 @@ public EventParticipantAddedHandler( public async Task HandleAsync(EventParticipantAdded eventArgs, CancellationToken cancellationToken) { - var participantNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventArgs.ParticipantId); + var participantNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventArgs.ParticipantId); var participant = await _studentsServiceClient.GetAsync(eventArgs.ParticipantId); if (participantNotifications == null) { - participantNotifications = new StudentNotifications(eventArgs.ParticipantId); + participantNotifications = new UserNotifications(eventArgs.ParticipantId); } var eventDetails = await _eventsServiceClient.GetEventAsync(eventArgs.EventId); @@ -109,10 +109,10 @@ public async Task HandleAsync(EventParticipantAdded eventArgs, CancellationToken details: detailsHtmlForOrganizer ); - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventDetails.Organizer.Id); + var organizerNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventDetails.Organizer.Id); if (organizerNotifications == null) { - organizerNotifications = new StudentNotifications(eventDetails.Organizer.Id); + organizerNotifications = new UserNotifications(eventDetails.Organizer.Id); } organizerNotifications.AddNotification(organizerNotification); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventParticipantRemovedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventParticipantRemovedHandler.cs similarity index 92% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventParticipantRemovedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventParticipantRemovedHandler.cs index 2bc1f5b7c..e557e3f75 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventParticipantRemovedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/EventParticipantRemovedHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,12 +11,12 @@ using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Core.Repositories; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers { public class EventParticipantRemovedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; private readonly ILogger _logger; @@ -24,7 +24,7 @@ public class EventParticipantRemovedHandler : IEventHandler logger, @@ -40,11 +40,11 @@ public EventParticipantRemovedHandler( public async Task HandleAsync(EventParticipantRemoved eventArgs, CancellationToken cancellationToken) { - var participantNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventArgs.Participant); + var participantNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventArgs.Participant); var participant = await _studentsServiceClient.GetAsync(eventArgs.Participant); if (participantNotifications == null) { - participantNotifications = new StudentNotifications(eventArgs.Participant); + participantNotifications = new UserNotifications(eventArgs.Participant); } var eventDetails = await _eventsServiceClient.GetEventAsync(eventArgs.EventId); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentCancelledInterestInEventHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentCancelledInterestInEventHandler.cs similarity index 93% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentCancelledInterestInEventHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentCancelledInterestInEventHandler.cs index 3496c177c..49aacfbe2 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentCancelledInterestInEventHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentCancelledInterestInEventHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using System.Threading; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,12 +11,12 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers { public class StudentCancelledInterestInEventHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; private readonly IHubContext _hubContext; @@ -24,7 +24,7 @@ public class StudentCancelledInterestInEventHandler : IEventHandler hubContext, @@ -54,10 +54,10 @@ public async Task HandleAsync(StudentCancelledInterestInEvent eventArgs, Cancell return; } - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventArgs.StudentId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventArgs.StudentId); if (studentNotifications == null) { - studentNotifications = new StudentNotifications(eventArgs.StudentId); + studentNotifications = new UserNotifications(eventArgs.StudentId); } var detailsHtml = $"

{student.FirstName} {student.LastName}, you have cancelled your interest in the event '{eventDetails.Name}' on {eventDetails.StartDate:yyyy-MM-dd}.

"; @@ -119,10 +119,10 @@ public async Task HandleAsync(StudentCancelledInterestInEvent eventArgs, Cancell details: organizerDetailsHtml ); - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventDetails.Organizer.Id); + var organizerNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventDetails.Organizer.Id); if (organizerNotifications == null) { - organizerNotifications = new StudentNotifications(eventDetails.Organizer.Id); + organizerNotifications = new UserNotifications(eventDetails.Organizer.Id); } organizerNotifications.AddNotification(organizerNotification); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentCancelledSignUpToEventHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentCancelledSignUpToEventHandler.cs similarity index 93% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentCancelledSignUpToEventHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentCancelledSignUpToEventHandler.cs index 3893d1f32..abb424a60 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentCancelledSignUpToEventHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentCancelledSignUpToEventHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using System.Threading; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,12 +11,12 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers { public class StudentCancelledSignUpToEventHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; private readonly IHubContext _hubContext; @@ -24,7 +24,7 @@ public class StudentCancelledSignUpToEventHandler : IEventHandler hubContext, @@ -54,10 +54,10 @@ public async Task HandleAsync(StudentCancelledSignUpToEvent eventArgs, Cancellat return; } - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventArgs.StudentId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventArgs.StudentId); if (studentNotifications == null) { - studentNotifications = new StudentNotifications(eventArgs.StudentId); + studentNotifications = new UserNotifications(eventArgs.StudentId); } var detailsHtml = $"

{student.FirstName} {student.LastName}, you have cancelled your registration for the event '{eventDetails.Name}' on {eventDetails.StartDate:yyyy-MM-dd}.

"; @@ -119,10 +119,10 @@ public async Task HandleAsync(StudentCancelledSignUpToEvent eventArgs, Cancellat details: organizerDetailsHtml ); - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventDetails.Organizer.Id); + var organizerNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventDetails.Organizer.Id); if (organizerNotifications == null) { - organizerNotifications = new StudentNotifications(eventDetails.Organizer.Id); + organizerNotifications = new UserNotifications(eventDetails.Organizer.Id); } organizerNotifications.AddNotification(organizerNotification); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentShowedInterestInEventHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentShowedInterestInEventHandler.cs similarity index 93% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentShowedInterestInEventHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentShowedInterestInEventHandler.cs index afeb9d8d5..e6801e834 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentShowedInterestInEventHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentShowedInterestInEventHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using System.Threading; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,12 +11,12 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers { public class StudentShowedInterestInEventHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; private readonly IHubContext _hubContext; @@ -24,7 +24,7 @@ public class StudentShowedInterestInEventHandler : IEventHandler hubContext, @@ -54,10 +54,10 @@ public async Task HandleAsync(StudentShowedInterestInEvent eventArgs, Cancellati return; } - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventArgs.StudentId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventArgs.StudentId); if (studentNotifications == null) { - studentNotifications = new StudentNotifications(eventArgs.StudentId); + studentNotifications = new UserNotifications(eventArgs.StudentId); } var detailsHtml = $"

{student.FirstName} {student.LastName}, you have shown interest in the event '{eventDetails.Name}' on {eventDetails.StartDate:yyyy-MM-dd}.

"; @@ -120,10 +120,10 @@ public async Task HandleAsync(StudentShowedInterestInEvent eventArgs, Cancellati details: detailsHtmlForOrganizer ); - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventDetails.Organizer.Id); + var organizerNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventDetails.Organizer.Id); if (organizerNotifications == null) { - organizerNotifications = new StudentNotifications(eventDetails.Organizer.Id); + organizerNotifications = new UserNotifications(eventDetails.Organizer.Id); } organizerNotifications.AddNotification(organizerNotification); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentSignedUpToEventHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentSignedUpToEventHandler.cs similarity index 93% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentSignedUpToEventHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentSignedUpToEventHandler.cs index ec1c6fe78..c5498a12d 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/StudentSignedUpToEventHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/Handlers/StudentSignedUpToEventHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using System.Threading; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,12 +11,12 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Events.Handlers { public class StudentSignedUpToEventHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventsServiceClient _eventsServiceClient; private readonly IHubContext _hubContext; @@ -24,7 +24,7 @@ public class StudentSignedUpToEventHandler : IEventHandler hubContext, @@ -54,10 +54,10 @@ public async Task HandleAsync(StudentSignedUpToEvent eventArgs, CancellationToke return; } - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventArgs.StudentId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventArgs.StudentId); if (studentNotifications == null) { - studentNotifications = new StudentNotifications(eventArgs.StudentId); + studentNotifications = new UserNotifications(eventArgs.StudentId); } var detailsHtml = $"

{student.FirstName} {student.LastName}, you have signed up for the event '{eventDetails.Name}' on {eventDetails.StartDate:yyyy-MM-dd}.

"; @@ -120,10 +120,10 @@ public async Task HandleAsync(StudentSignedUpToEvent eventArgs, CancellationToke details: detailsHtmlForOrganizer ); - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventDetails.Organizer.Id); + var organizerNotifications = await _studentNotificationsRepository.GetByUserIdAsync(eventDetails.Organizer.Id); if (organizerNotifications == null) { - organizerNotifications = new StudentNotifications(eventDetails.Organizer.Id); + organizerNotifications = new UserNotifications(eventDetails.Organizer.Id); } organizerNotifications.AddNotification(organizerNotification); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentCancelledInterestInEvent.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentCancelledInterestInEvent.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentCancelledInterestInEvent.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentCancelledInterestInEvent.cs index 89d40471a..d301a22e7 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentCancelledInterestInEvent.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentCancelledInterestInEvent.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Events { [Message("events")] public class StudentCancelledInterestInEvent: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentCancelledSignUpToEvent.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentCancelledSignUpToEvent.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentCancelledSignUpToEvent.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentCancelledSignUpToEvent.cs index 04e3b172d..1390319d0 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentCancelledSignUpToEvent.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentCancelledSignUpToEvent.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Events { [Message("events")] public class StudentCancelledSignUpToEvent: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentShowedInterestInEvent.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentShowedInterestInEvent.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentShowedInterestInEvent.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentShowedInterestInEvent.cs index 5821e4ac3..09a410d2f 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentShowedInterestInEvent.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentShowedInterestInEvent.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Events { [Message("events")] public class StudentShowedInterestInEvent: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentSignedUpToEvent.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentSignedUpToEvent.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentSignedUpToEvent.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentSignedUpToEvent.cs index 7d6fa1009..dccbf9fbf 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/StudentSignedUpToEvent.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Events/StudentSignedUpToEvent.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Events { [Message("events")] public class StudentSignedUpToEvent: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendAdded.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendAdded.cs similarity index 82% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendAdded.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendAdded.cs index e9be8b8d6..810a291e8 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendAdded.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendAdded.cs @@ -1,9 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends { - [Contract] public class FriendAdded : IEvent { public Guid RequesterId { get; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendInvited.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendInvited.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendInvited.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendInvited.cs index 1a0bb6880..028b09f14 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendInvited.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendInvited.cs @@ -1,8 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using MiniSpace.Services.Notifications.Core.Events; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends { [Message("friends")] public class FriendInvited : IEvent, IDomainEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendRequestCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendRequestCreated.cs similarity index 83% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendRequestCreated.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendRequestCreated.cs index 14432b285..faf1ce7aa 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendRequestCreated.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendRequestCreated.cs @@ -1,10 +1,9 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends { - [Contract] public class FriendRequestCreated : IEvent { public Guid RequesterId { get; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendRequestSent.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendRequestSent.cs similarity index 82% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendRequestSent.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendRequestSent.cs index 59205c791..879f926ae 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/FriendRequestSent.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/FriendRequestSent.cs @@ -1,9 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends { - [Contract] public class FriendRequestSent : IEvent { public Guid InviterId { get; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendAddedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendAddedHandler.cs similarity index 96% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendAddedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendAddedHandler.cs index c1d7c0d56..5056ca692 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendAddedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendAddedHandler.cs @@ -1,11 +1,11 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; using System.Collections.Generic; using MiniSpace.Services.Notifications.Application.Exceptions; // This event handler is not used! ⁉️ -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends.Handlers { public class FriendAddedHandler : IEventHandler { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendInvitedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendInvitedHandler.cs similarity index 89% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendInvitedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendInvitedHandler.cs index 888e257c8..d36615b13 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendInvitedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendInvitedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; @@ -11,12 +11,12 @@ using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends.Handlers { public class FriendInvitedHandler : IEventHandler { private readonly INotificationRepository _notificationRepository; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IEventMapper _eventMapper; private readonly IMessageBroker _messageBroker; @@ -25,7 +25,7 @@ public class FriendInvitedHandler : IEventHandler public FriendInvitedHandler( INotificationRepository notificationRepository, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, IEventMapper eventMapper, IMessageBroker messageBroker, @@ -65,7 +65,7 @@ public async Task HandleAsync(FriendInvited @event, CancellationToken cancellati - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(@event.InviteeId) ?? new StudentNotifications(@event.InviteeId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(@event.InviteeId) ?? new UserNotifications(@event.InviteeId); studentNotifications.AddNotification(notification); await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); @@ -92,14 +92,7 @@ public async Task HandleAsync(FriendInvited @event, CancellationToken cancellati await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); _logger.LogInformation($"Sent SignalR notification to all users with user id UserId={@event.InviteeId}."); - - var serializedEvent = JsonSerializer.Serialize(notificationCreatedEvent, new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }); - await _messageBroker.PublishAsync(notificationCreatedEvent); } - } } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendRequestCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendRequestCreatedHandler.cs similarity index 96% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendRequestCreatedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendRequestCreatedHandler.cs index afa812cb0..cd921f5ae 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendRequestCreatedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendRequestCreatedHandler.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Hubs; @@ -12,7 +12,7 @@ using System.Threading.Tasks; using System.Threading; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends.Handlers { public class FriendRequestCreatedHandler : IEventHandler { @@ -106,7 +106,6 @@ public async Task HandleAsync(FriendRequestCreated friendEvent, CancellationToke ); await _messageBroker.PublishAsync(notificationCreatedEvent); - _logger.LogInformation($"Published NotificationCreated event for NotificationId={notification.NotificationId}"); } } } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendRequestSentHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendRequestSentHandler.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendRequestSentHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendRequestSentHandler.cs index 0f1e93a13..e2be5a18d 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/FriendRequestSentHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/FriendRequestSentHandler.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; @@ -12,19 +12,19 @@ using MiniSpace.Services.Notifications.Application.Hubs; using MiniSpace.Services.Notifications.Application.Dto; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends.Handlers { public class FriendRequestSentHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly ILogger _logger; private readonly IHubContext _hubContext; public FriendRequestSentHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, ILogger logger, IHubContext hubContext) @@ -60,7 +60,7 @@ public async Task HandleAsync(FriendRequestSent @event, CancellationToken cancel details: detailsHtml ); - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(@event.InviteeId) ?? new StudentNotifications(@event.InviteeId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(@event.InviteeId) ?? new UserNotifications(@event.InviteeId); studentNotifications.AddNotification(notification); await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); @@ -87,11 +87,7 @@ public async Task HandleAsync(FriendRequestSent @event, CancellationToken cancel await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); _logger.LogInformation("Sent SignalR notification to all users."); - // await NotificationHub.SendNotification(_hubContext, @event.InviteeId.ToString(), notificationDto, _logger); - // _logger.LogInformation($"Sent SignalR notification to UserId={@event.InviteeId}"); - await _messageBroker.PublishAsync(notificationCreatedEvent); - _logger.LogInformation($"Published NotificationCreated event for UserId={notification.UserId}"); } } } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PendingFriendRequestAcceptedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/PendingFriendRequestAcceptedHandler.cs similarity index 93% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PendingFriendRequestAcceptedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/PendingFriendRequestAcceptedHandler.cs index 1a7ef5d67..da500a199 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PendingFriendRequestAcceptedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/Handlers/PendingFriendRequestAcceptedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Application.Services.Clients; @@ -12,18 +12,18 @@ using MiniSpace.Services.Notifications.Application.Dto; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends.Handlers { public class PendingFriendRequestAcceptedHandler : IEventHandler { - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IMessageBroker _messageBroker; private readonly IStudentsServiceClient _studentsServiceClient; private readonly ILogger _logger; private readonly IHubContext _hubContext; public PendingFriendRequestAcceptedHandler( - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IMessageBroker messageBroker, IStudentsServiceClient studentsServiceClient, ILogger logger, @@ -62,10 +62,10 @@ public async Task HandleAsync(PendingFriendAccepted @event, CancellationToken ca details: detailsHtml ); - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(@event.RequesterId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(@event.RequesterId); if (studentNotifications == null) { - studentNotifications = new StudentNotifications(@event.RequesterId); + studentNotifications = new UserNotifications(@event.RequesterId); _logger.LogInformation($"Creating new StudentNotifications for UserId={@event.RequesterId}"); } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PendingFriendAccepted.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/PendingFriendAccepted.cs similarity index 82% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PendingFriendAccepted.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/PendingFriendAccepted.cs index 1bcbb7286..b46d16810 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PendingFriendAccepted.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/PendingFriendAccepted.cs @@ -1,9 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends { - [Contract] public class PendingFriendAccepted : IEvent { public Guid RequesterId { get; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PendingFriendDeclined.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/PendingFriendDeclined.cs similarity index 84% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PendingFriendDeclined.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/PendingFriendDeclined.cs index df1e2fb52..b6838a371 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PendingFriendDeclined.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Friends/PendingFriendDeclined.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Friends { [Message("friends")] public class PendingFriendDeclined : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/CommentCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/CommentCreatedHandler.cs deleted file mode 100644 index 029508793..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/CommentCreatedHandler.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using Convey.CQRS.Events; -using MiniSpace.Services.Notifications.Core.Repositories; -using MiniSpace.Services.Notifications.Application.Services; -using MiniSpace.Services.Notifications.Core.Entities; -using System.Threading.Tasks; -using System.Threading; -using MiniSpace.Services.Notifications.Application.Services.Clients; -using Microsoft.Extensions.Logging; -using Microsoft.AspNetCore.SignalR; -using MiniSpace.Services.Notifications.Application.Hubs; -using MiniSpace.Services.Notifications.Application.Dto; - -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers -{ - public class CommentCreatedHandler : IEventHandler - { - private readonly IMessageBroker _messageBroker; - private readonly IStudentsServiceClient _studentsServiceClient; - private readonly IEventsServiceClient _eventsServiceClient; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; - private readonly ICommentsServiceClient _commentsServiceClient; - private readonly ILogger _logger; - private readonly IHubContext _hubContext; - - public CommentCreatedHandler( - IMessageBroker messageBroker, - IStudentsServiceClient studentsServiceClient, - IEventsServiceClient eventsServiceClient, - IStudentNotificationsRepository studentNotificationsRepository, - ICommentsServiceClient commentsServiceClient, - ILogger logger, - IHubContext hubContext) - { - _messageBroker = messageBroker; - _studentsServiceClient = studentsServiceClient; - _eventsServiceClient = eventsServiceClient; - _studentNotificationsRepository = studentNotificationsRepository; - _commentsServiceClient = commentsServiceClient; - _logger = logger; - _hubContext = hubContext; - } - - public async Task HandleAsync(CommentCreated eventArgs, CancellationToken cancellationToken) - { - CommentDto commentDetails; - try - { - commentDetails = await _commentsServiceClient.GetCommentAsync(eventArgs.CommentId); - if (commentDetails == null) - { - _logger.LogError("No comment details found."); - return; - } - } - catch (Exception ex) - { - _logger.LogError($"Failed to retrieve comment details: {ex.Message}"); - throw; - } - - EventDto eventDetails; - try - { - eventDetails = await _eventsServiceClient.GetEventAsync(commentDetails.ContextId); - if (eventDetails == null) - { - _logger.LogError("Event details for comment context not found."); - return; - } - } - catch (Exception ex) - { - _logger.LogError($"Failed to retrieve event details for comment context: {ex.Message}"); - throw; - } - - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(commentDetails.StudentId); - if (studentNotifications == null) - { - studentNotifications = new StudentNotifications(commentDetails.StudentId); - } - - var userNotification = new Notification( - notificationId: Guid.NewGuid(), - userId: commentDetails.StudentId, - message: $"Thank you for your comment on the event '{eventDetails.Name}'.", - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: eventArgs.CommentId, - eventType: NotificationEventType.CommentCreated - ); - - studentNotifications.AddNotification(userNotification); - await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); - - var userNotificationDetailsHtml = $"

Your comment on the event '{eventDetails.Name}' has been posted successfully.

"; - - var notificationCreatedEvent = new NotificationCreated( - notificationId: Guid.NewGuid(), - userId: commentDetails.StudentId, - message: $"Thank you for your comment on the event '{eventDetails.Name}'.", - createdAt: DateTime.UtcNow, - eventType: NotificationEventType.CommentCreated.ToString(), - relatedEntityId: eventArgs.CommentId, - details: userNotificationDetailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - - var notificationDto = new NotificationDto - { - UserId = commentDetails.StudentId, - Message = $"Thank you for your comment on the event '{eventDetails.Name}'.", - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.CommentCreated, - RelatedEntityId = eventArgs.CommentId, - Details = userNotificationDetailsHtml - }; - - await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation("Broadcasted SignalR notification to all users."); - - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(eventDetails.Organizer.Id); - if (organizerNotifications == null) - { - organizerNotifications = new StudentNotifications(eventDetails.Organizer.Id); - } - - var organizerNotification = new Notification( - notificationId: Guid.NewGuid(), - userId: eventDetails.Organizer.Id, - message: $"A new comment has been posted by {commentDetails.StudentName} on your event '{eventDetails.Name}'.", - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: eventArgs.CommentId, - eventType: NotificationEventType.CommentCreated - ); - - organizerNotifications.AddNotification(organizerNotification); - await _studentNotificationsRepository.AddOrUpdateAsync(organizerNotifications); - - var organizerNotificationDetailsHtml = $"

{commentDetails.StudentName} commented on your event '{eventDetails.Name}': {commentDetails.CommentContext}

"; - - var organizerNotificationCreatedEvent = new NotificationCreated( - notificationId: Guid.NewGuid(), - userId: eventDetails.Organizer.Id, - message: $"A new comment has been posted by {commentDetails.StudentName} on your event '{eventDetails.Name}'.", - createdAt: organizerNotification.CreatedAt, - eventType: NotificationEventType.CommentCreated.ToString(), - relatedEntityId: eventArgs.CommentId, - details: organizerNotificationDetailsHtml - ); - - await _messageBroker.PublishAsync(organizerNotificationCreatedEvent); - - var organizerNotificationDto = new NotificationDto - { - UserId = eventDetails.Organizer.Id, - Message = $"A new comment has been posted by {commentDetails.StudentName} on your event '{eventDetails.Name}'.", - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.CommentCreated, - RelatedEntityId = eventArgs.CommentId, - Details = organizerNotificationDetailsHtml - }; - - await NotificationHub.BroadcastNotification(_hubContext, organizerNotificationDto, _logger); - _logger.LogInformation("Broadcasted SignalR notification to all users."); - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventCreatedHandler.cs deleted file mode 100644 index f4fbeea70..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventCreatedHandler.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Convey.CQRS.Events; -using MiniSpace.Services.Notifications.Core.Repositories; -using MiniSpace.Services.Notifications.Application.Services; -using MiniSpace.Services.Notifications.Core.Entities; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Threading; -using System.Text.Json; -using MiniSpace.Services.Notifications.Application.Services.Clients; -using MiniSpace.Services.Notifications.Application.Dto; - -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers -{ - public class EventCreatedHandler : IEventHandler - { - private readonly IMessageBroker _messageBroker; - private readonly IStudentsServiceClient _studentsServiceClient; - private readonly IEventsServiceClient _eventsServiceClient; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; - - public EventCreatedHandler( - IMessageBroker messageBroker, - IStudentsServiceClient studentsServiceClient, - IEventsServiceClient eventsServiceClient, - IStudentNotificationsRepository studentNotificationsRepository) - { - _messageBroker = messageBroker; - _studentsServiceClient = studentsServiceClient; - _eventsServiceClient = eventsServiceClient; - _studentNotificationsRepository = studentNotificationsRepository; - } - - public async Task HandleAsync(EventCreated eventCreated, CancellationToken cancellationToken) - { - IEnumerable users; - - try - { - users = await _studentsServiceClient.GetAllAsync(); - } - catch (Exception ex) - { - // Console.WriteLine($"Failed to get users: {ex.Message}"); - throw; - } - - EventDto eventDetails; - try - { - eventDetails = await _eventsServiceClient.GetEventAsync(eventCreated.EventId); - } - catch (Exception ex) - { - Console.WriteLine($"Failed to retrieve event details: {ex.Message}"); - throw; - } - - if (users == null) - { - // Console.WriteLine("No users found."); - return; - } - - foreach (var user in users) - { - var notificationMessage = $"A new event '{eventDetails.Name}' organized by {eventDetails.Organizer.Name} is scheduled from {eventDetails.StartDate:yyyy-MM-dd} to {eventDetails.EndDate:yyyy-MM-dd} with organization {eventDetails.Organizer.OrganizationName} at {eventDetails.Location.Street}, {eventDetails.Location.City} . This event offers a capacity of {eventDetails.Capacity} with a registration fee of ${eventDetails.Fee}. {eventDetails.Description}"; - var detailsHtml = $"

Check out the new event details here.

"; - - - var notification = new Notification( - notificationId: Guid.NewGuid(), - userId: user.Id, - message: notificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: eventCreated.EventId, - eventType: NotificationEventType.NewEvent - ); - - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(user.Id); - if (studentNotifications == null) - { - studentNotifications = new StudentNotifications(user.Id); - } - - - studentNotifications.AddNotification(notification); - await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); - - var notificationCreatedEvent = new NotificationCreated( - notificationId: Guid.NewGuid(), - userId: user.Id, - message: notificationMessage, - createdAt: DateTime.UtcNow, - eventType: NotificationEventType.NewEvent.ToString(), - relatedEntityId: eventCreated.EventId, - details: detailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - } - } - } -} - diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventDeletedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventDeletedHandler.cs deleted file mode 100644 index fdf8e3c51..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EventDeletedHandler.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; -using MiniSpace.Services.Notifications.Application.Dto; -using MiniSpace.Services.Notifications.Application.Hubs; -using MiniSpace.Services.Notifications.Application.Services; -using MiniSpace.Services.Notifications.Application.Services.Clients; -using MiniSpace.Services.Notifications.Core.Entities; -using MiniSpace.Services.Notifications.Core.Repositories; - -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers -{ - public class EventDeletedHandler : IEventHandler - { - private readonly IMessageBroker _messageBroker; - private readonly IEventsServiceClient _eventsServiceClient; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; - private readonly IStudentsServiceClient _studentsServiceClient; - private readonly ILogger _logger; - private readonly IHubContext _hubContext; - - public EventDeletedHandler( - IMessageBroker messageBroker, - IEventsServiceClient eventsServiceClient, - IStudentNotificationsRepository studentNotificationsRepository, - IStudentsServiceClient studentsServiceClient, - ILogger logger, - IHubContext hubContext) - { - _messageBroker = messageBroker; - _eventsServiceClient = eventsServiceClient; - _studentNotificationsRepository = studentNotificationsRepository; - _studentsServiceClient = studentsServiceClient; - _logger = logger; - _hubContext = hubContext; - } - - public async Task HandleAsync(EventDeleted eventDeleted, CancellationToken cancellationToken) - { - EventDto eventDetails; - try - { - eventDetails = await _eventsServiceClient.GetEventAsync(eventDeleted.EventId); - if (eventDetails == null) - { - _logger.LogError($"Event with ID {eventDeleted.EventId} not found."); - return; - } - } - catch (Exception ex) - { - _logger.LogError($"Failed to retrieve event details: {ex.Message}"); - return; - } - - EventParticipantsDto eventParticipants; - try - { - eventParticipants = await _eventsServiceClient.GetParticipantsAsync(eventDeleted.EventId); - if (eventParticipants == null) - { - _logger.LogError($"No participants found for event with ID {eventDeleted.EventId}."); - return; - } - } - catch (Exception ex) - { - _logger.LogError($"Failed to retrieve participants for event with ID {eventDeleted.EventId}: {ex.Message}"); - return; - } - - foreach (var studentParticipant in eventParticipants.SignedUpStudents) - { - StudentDto student; - try - { - student = await _studentsServiceClient.GetAsync(studentParticipant.StudentId); - if (student == null) - { - _logger.LogWarning($"Student with ID {studentParticipant.StudentId} not found."); - continue; - } - } - catch (Exception ex) - { - _logger.LogError($"Failed to retrieve student with ID {studentParticipant.StudentId}: {ex.Message}"); - continue; - } - - var notificationMessage = $"The event you were signed up for has been cancelled."; - var detailsHtml = $"

Event {eventDetails.Name} scheduled on {eventDetails.StartDate:yyyy-MM-dd} has been cancelled. We apologize for any inconvenience.

"; - - var notification = new Notification( - notificationId: Guid.NewGuid(), - userId: student.Id, - message: notificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: eventDeleted.EventId, - eventType: NotificationEventType.EventDeleted - ); - - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(student.Id); - if (studentNotifications == null) - { - studentNotifications = new StudentNotifications(student.Id); - } - - studentNotifications.AddNotification(notification); - await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); - - var notificationCreatedEvent = new NotificationCreated( - notification.NotificationId, - student.Id, - notificationMessage, - DateTime.UtcNow, - NotificationEventType.EventDeleted.ToString(), - eventDeleted.EventId, - detailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - - var notificationDto = new NotificationDto - { - UserId = student.Id, - Message = notificationMessage, - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.EventDeleted, - RelatedEntityId = eventDeleted.EventId, - Details = detailsHtml - }; - - await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation($"Broadcasted SignalR notification to student with ID {student.Id}."); - } - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PostCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PostCreatedHandler.cs deleted file mode 100644 index 376975fd3..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PostCreatedHandler.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Threading; -using Convey.CQRS.Events; -using MiniSpace.Services.Notifications.Application.Services.Clients; -using MiniSpace.Services.Notifications.Core.Repositories; -using MiniSpace.Services.Notifications.Core.Entities; -using MiniSpace.Services.Notifications.Application.Dto; -using MiniSpace.Services.Notifications.Application.Services; -using Microsoft.AspNetCore.SignalR; -using MiniSpace.Services.Notifications.Application.Hubs; -using Microsoft.Extensions.Logging; -using MiniSpace.Services.Notifications.Application.DTO; - -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers -{ - public class PostCreatedHandler : IEventHandler - { - private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; - private readonly IEventsServiceClient _eventsServiceClient; - private readonly IPostsServiceClient _postsServiceClient; - private readonly IStudentsServiceClient _studentsServiceClient; - private readonly IHubContext _hubContext; - private readonly ILogger _logger; - - public PostCreatedHandler( - IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, - IEventsServiceClient eventsServiceClient, - IPostsServiceClient postsServiceClient, - IStudentsServiceClient studentsServiceClient, - IHubContext hubContext, - ILogger logger) - { - _messageBroker = messageBroker; - _studentNotificationsRepository = studentNotificationsRepository; - _eventsServiceClient = eventsServiceClient; - _postsServiceClient = postsServiceClient; - _studentsServiceClient = studentsServiceClient; - _hubContext = hubContext; - _logger = logger; - } - - public async Task HandleAsync(PostCreated eventArgs, CancellationToken cancellationToken) - { - var post = await _postsServiceClient.GetPostAsync(eventArgs.PostId); - if (post == null) - { - _logger.LogError("Post not found."); - return; - } - - var eventDetails = await _eventsServiceClient.GetEventAsync(post.EventId); - if (eventDetails == null) - { - _logger.LogError("Event not found for the post."); - return; - } - - var eventParticipants = await _eventsServiceClient.GetParticipantsAsync(post.EventId); - if (eventParticipants == null) - { - _logger.LogError("No participants found for the event."); - return; - } - - foreach (var studentParticipant in eventParticipants.InterestedStudents) - { - var student = await _studentsServiceClient.GetAsync(studentParticipant.StudentId); - if (student != null) - { - await NotifyStudent(student, eventDetails, post); - } - } - - foreach (var studentParticipant in eventParticipants.SignedUpStudents) - { - var student = await _studentsServiceClient.GetAsync(studentParticipant.StudentId); - if (student != null) - { - await NotifyStudent(student, eventDetails, post); - } - } - - // Notify the organizer - await NotifyOrganizer(eventDetails.Organizer, eventDetails, post); - } - - private async Task NotifyStudent(StudentDto student, EventDto eventDetails, PostDto post) - { - var notificationMessage = $"A new post has been created for an event you are interested in: '{eventDetails.Name}'."; - var detailsHtml = $"

{notificationMessage} Check out the post details here: {post.TextContent}

"; - - var notification = new Notification( - notificationId: Guid.NewGuid(), - userId: student.Id, - message: notificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: post.EventId, - eventType: NotificationEventType.PostCreated, - details: detailsHtml - ); - - _logger.LogInformation($"Creating post creation notification for user: {student.Id}"); - - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(student.Id); - if (studentNotifications == null) - { - studentNotifications = new StudentNotifications(student.Id); - } - - studentNotifications.AddNotification(notification); - await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); - - var notificationCreatedEvent = new NotificationCreated( - notification.NotificationId, - student.Id, - notificationMessage, - DateTime.UtcNow, - NotificationEventType.PostCreated.ToString(), - post.EventId, - detailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - - var notificationDto = new NotificationDto - { - UserId = student.Id, - Message = notificationMessage, - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.PostCreated, - RelatedEntityId = post.EventId, - Details = detailsHtml - }; - - // Broadcast SignalR notification - await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation($"Broadcasted SignalR notification to student with ID {student.Id}."); - } - - private async Task NotifyOrganizer(OrganizerDto organizer, EventDto eventDetails, PostDto post) - { - var notificationMessage = $"A new post has been created for your event '{eventDetails.Name}'."; - var detailsHtml = $"

{notificationMessage} View the new post here: {post.TextContent}

"; - - var notification = new Notification( - notificationId: Guid.NewGuid(), - userId: organizer.Id, - message: notificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: post.EventId, - eventType: NotificationEventType.PostCreated, - details: detailsHtml - ); - - _logger.LogInformation($"Creating post creation notification for organizer: {organizer.Id}"); - - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(organizer.Id); - if (organizerNotifications == null) - { - organizerNotifications = new StudentNotifications(organizer.Id); - } - - organizerNotifications.AddNotification(notification); - await _studentNotificationsRepository.AddOrUpdateAsync(organizerNotifications); - - var notificationCreatedEvent = new NotificationCreated( - notification.NotificationId, - organizer.Id, - notificationMessage, - DateTime.UtcNow, - NotificationEventType.PostCreated.ToString(), - post.EventId, - detailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - - var notificationDto = new NotificationDto - { - UserId = organizer.Id, - Message = notificationMessage, - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.PostCreated, - RelatedEntityId = post.EventId, - Details = detailsHtml - }; - - // Broadcast SignalR notification - await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation($"Broadcasted SignalR notification to organizer with ID {organizer.Id}."); - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PostUpdatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PostUpdatedHandler.cs deleted file mode 100644 index 802fc603b..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PostUpdatedHandler.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; -using MiniSpace.Services.Notifications.Application.Dto; -using MiniSpace.Services.Notifications.Application.Hubs; -using MiniSpace.Services.Notifications.Application.Services.Clients; -using MiniSpace.Services.Notifications.Core.Entities; -using MiniSpace.Services.Notifications.Core.Repositories; -using MiniSpace.Services.Notifications.Application.Services; -using MiniSpace.Services.Notifications.Application.DTO; - -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers -{ - public class PostUpdatedHandler : IEventHandler - { - private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; - private readonly IEventsServiceClient _eventsServiceClient; - private readonly IPostsServiceClient _postsServiceClient; - private readonly IStudentsServiceClient _studentsServiceClient; - private readonly IHubContext _hubContext; - private readonly ILogger _logger; - - public PostUpdatedHandler( - IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, - IEventsServiceClient eventsServiceClient, - IPostsServiceClient postsServiceClient, - IStudentsServiceClient studentsServiceClient, - IHubContext hubContext, - ILogger logger) - { - _messageBroker = messageBroker; - _studentNotificationsRepository = studentNotificationsRepository; - _eventsServiceClient = eventsServiceClient; - _postsServiceClient = postsServiceClient; - _studentsServiceClient = studentsServiceClient; - _hubContext = hubContext; - _logger = logger; - } - - public async Task HandleAsync(PostUpdated eventArgs, CancellationToken cancellationToken) - { - var post = await _postsServiceClient.GetPostAsync(eventArgs.PostId); - if (post == null) - { - _logger.LogError("Post not found."); - return; - } - - var eventDetails = await _eventsServiceClient.GetEventAsync(post.EventId); - if (eventDetails == null) - { - _logger.LogError("Event not found for the post."); - return; - } - - var eventParticipants = await _eventsServiceClient.GetParticipantsAsync(post.EventId); - if (eventParticipants == null) - { - _logger.LogError("No participants found for the event."); - return; - } - - foreach (var studentParticipant in eventParticipants.InterestedStudents) - { - var student = await _studentsServiceClient.GetAsync(studentParticipant.StudentId); - if (student != null) - { - await NotifyStudent(student, eventDetails, post); - } - } - - foreach (var studentParticipant in eventParticipants.SignedUpStudents) - { - var student = await _studentsServiceClient.GetAsync(studentParticipant.StudentId); - if (student != null) - { - await NotifyStudent(student, eventDetails, post); - } - } - - // Notify the organizer - await NotifyOrganizer(eventDetails.Organizer, eventDetails, post); - } - - private async Task NotifyStudent(StudentDto student, EventDto eventDetails, PostDto post) - { - var notificationMessage = $"An updated post is available for an event you are interested in: '{eventDetails.Name}'."; - var detailsHtml = $"

{notificationMessage} Check out the updated post details here: {post.TextContent}

"; - - var notification = new Notification( - notificationId: Guid.NewGuid(), - userId: student.Id, - message: notificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: post.EventId, - eventType: NotificationEventType.PostUpdated, - details: detailsHtml - ); - - _logger.LogInformation($"Creating post update notification for user: {student.Id}"); - - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(student.Id); - if (studentNotifications == null) - { - studentNotifications = new StudentNotifications(student.Id); - } - - studentNotifications.AddNotification(notification); - await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); - - var notificationCreatedEvent = new NotificationCreated( - notification.NotificationId, - student.Id, - notificationMessage, - DateTime.UtcNow, - NotificationEventType.PostUpdated.ToString(), - post.EventId, - detailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - - var notificationDto = new NotificationDto - { - UserId = student.Id, - Message = notificationMessage, - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.PostUpdated, - RelatedEntityId = post.EventId, - Details = detailsHtml - }; - - // Broadcast SignalR notification - await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation($"Broadcasted SignalR notification to student with ID {student.Id}."); - } - - private async Task NotifyOrganizer(OrganizerDto organizer, EventDto eventDetails, PostDto post) - { - var notificationMessage = $"An updated post is available for your event '{eventDetails.Name}'."; - var detailsHtml = $"

{notificationMessage} View the updated post here: {post.TextContent}

"; - - var notification = new Notification( - notificationId: Guid.NewGuid(), - userId: organizer.Id, - message: notificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: post.EventId, - eventType: NotificationEventType.PostUpdated, - details: detailsHtml - ); - - _logger.LogInformation($"Creating post update notification for organizer: {organizer.Id}"); - - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(organizer.Id); - if (organizerNotifications == null) - { - organizerNotifications = new StudentNotifications(organizer.Id); - } - - organizerNotifications.AddNotification(notification); - await _studentNotificationsRepository.AddOrUpdateAsync(organizerNotifications); - - var notificationCreatedEvent = new NotificationCreated( - notification.NotificationId, - organizer.Id, - notificationMessage, - DateTime.UtcNow, - NotificationEventType.PostUpdated.ToString(), - post.EventId, - detailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - - var notificationDto = new NotificationDto - { - UserId = organizer.Id, - Message = notificationMessage, - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.PostUpdated, - RelatedEntityId = post.EventId, - Details = detailsHtml - }; - - // Broadcast SignalR notification - await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation($"Broadcasted SignalR notification to organizer with ID {organizer.Id}."); - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReactionCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReactionCreatedHandler.cs deleted file mode 100644 index 658775096..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReactionCreatedHandler.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using Microsoft.AspNetCore.SignalR; -using Microsoft.Extensions.Logging; -using MiniSpace.Services.Notifications.Application.Dto; -using MiniSpace.Services.Notifications.Application.Hubs; -using MiniSpace.Services.Notifications.Application.Services.Clients; -using MiniSpace.Services.Notifications.Core.Entities; -using MiniSpace.Services.Notifications.Core.Repositories; -using MiniSpace.Services.Notifications.Application.Services; - -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers -{ - public class ReactionCreatedHandler : IEventHandler - { - private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; - private readonly IReactionsServiceClient _reactionsServiceClient; - private readonly IEventsServiceClient _eventsServiceClient; - private readonly IPostsServiceClient _postsServiceClient; - private readonly IHubContext _hubContext; - private readonly ILogger _logger; - - public ReactionCreatedHandler( - IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, - IReactionsServiceClient reactionsServiceClient, - IEventsServiceClient eventsServiceClient, - IPostsServiceClient postsServiceClient, - IHubContext hubContext, - ILogger logger) - { - _messageBroker = messageBroker; - _studentNotificationsRepository = studentNotificationsRepository; - _reactionsServiceClient = reactionsServiceClient; - _eventsServiceClient = eventsServiceClient; - _postsServiceClient = postsServiceClient; - _hubContext = hubContext; - _logger = logger; - } - - public async Task HandleAsync(ReactionCreated eventArgs, CancellationToken cancellationToken) - { - var reactionDetails = await _reactionsServiceClient.GetReactionsAsync(); - var reaction = reactionDetails.FirstOrDefault(r => r.Id == eventArgs.ReactionId); - - if (reaction == null) - { - _logger.LogError("Reaction details not found."); - return; - } - - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(reaction.StudentId); - if (studentNotifications == null) - { - studentNotifications = new StudentNotifications(reaction.StudentId); - } - - var notificationMessage = "Your reaction has been recorded."; - var notificationDetailsHtml = "

Thank you for your reaction! Your interaction helps us to better understand what content resonates with our community.

"; - - var notification = new Notification( - notificationId: Guid.NewGuid(), - userId: reaction.StudentId, - message: notificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: reaction.ContentId, - eventType: NotificationEventType.ReactionAdded, - details: notificationDetailsHtml - ); - - studentNotifications.AddNotification(notification); - await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); - - var notificationCreatedEvent = new NotificationCreated( - notification.NotificationId, - reaction.StudentId, - notificationMessage, - DateTime.UtcNow, - NotificationEventType.ReactionAdded.ToString(), - reaction.ContentId, - notificationDetailsHtml - ); - - await _messageBroker.PublishAsync(notificationCreatedEvent); - - var notificationDto = new NotificationDto - { - UserId = reaction.StudentId, - Message = notificationMessage, - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.ReactionAdded, - RelatedEntityId = reaction.ContentId, - Details = notificationDetailsHtml - }; - - // Broadcast SignalR notification to the student - await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); - _logger.LogInformation($"Broadcasted SignalR notification to student with ID {reaction.StudentId}."); - - // Notify the organizer - Guid? organizerId = null; - if (reaction.ContentType == ReactionContentType.Event) - { - var eventDetails = await _eventsServiceClient.GetEventAsync(reaction.ContentId); - organizerId = eventDetails?.Organizer.Id; - } - else if (reaction.ContentType == ReactionContentType.Post) - { - var postDetails = await _postsServiceClient.GetPostAsync(reaction.ContentId); - organizerId = postDetails?.OrganizerId; - } - - if (organizerId.HasValue) - { - var organizerNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(organizerId.Value); - if (organizerNotifications == null) - { - organizerNotifications = new StudentNotifications(organizerId.Value); - } - - var organizerNotificationMessage = "A new reaction has been added to your content."; - var organizerNotificationDetailsHtml = $"

{reaction.StudentFullName} reacted to your content.

"; - - var organizerNotification = new Notification( - notificationId: Guid.NewGuid(), - userId: organizerId.Value, - message: organizerNotificationMessage, - status: NotificationStatus.Unread, - createdAt: DateTime.UtcNow, - updatedAt: null, - relatedEntityId: reaction.ContentId, - eventType: NotificationEventType.ReactionAdded, - details: organizerNotificationDetailsHtml - ); - - organizerNotifications.AddNotification(organizerNotification); - await _studentNotificationsRepository.AddOrUpdateAsync(organizerNotifications); - - var organizerNotificationCreatedEvent = new NotificationCreated( - organizerNotification.NotificationId, - organizerId.Value, - organizerNotificationMessage, - DateTime.UtcNow, - NotificationEventType.ReactionAdded.ToString(), - reaction.ContentId, - organizerNotificationDetailsHtml - ); - - await _messageBroker.PublishAsync(organizerNotificationCreatedEvent); - - var organizerNotificationDto = new NotificationDto - { - UserId = organizerId.Value, - Message = organizerNotificationMessage, - CreatedAt = DateTime.UtcNow, - EventType = NotificationEventType.ReactionAdded, - RelatedEntityId = reaction.ContentId, - Details = organizerNotificationDetailsHtml - }; - - // Broadcast SignalR notification to the organizer - await NotificationHub.BroadcastNotification(_hubContext, organizerNotificationDto, _logger); - _logger.LogInformation($"Broadcasted SignalR notification to organizer with ID {organizerId.Value}."); - } - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EmailVerified.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/EmailVerified.cs similarity index 86% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EmailVerified.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/EmailVerified.cs index 753772c2b..eb6c0b457 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/EmailVerified.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/EmailVerified.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity { [Message("identity")] public class EmailVerified : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EmailVerifiedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/EmailVerifiedHandler.cs similarity index 87% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EmailVerifiedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/EmailVerifiedHandler.cs index f9cdea30d..3209ed4a6 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/EmailVerifiedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/EmailVerifiedHandler.cs @@ -1,21 +1,21 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Core.Repositories; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity.Handlers { public class EmailVerifiedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; public EmailVerifiedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository) + IUserNotificationsRepository studentNotificationsRepository) { _messageBroker = messageBroker; _studentNotificationsRepository = studentNotificationsRepository; @@ -23,8 +23,8 @@ public EmailVerifiedHandler( public async Task HandleAsync(EmailVerified @event, CancellationToken cancellationToken) { - var userNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(@event.UserId) - ?? new StudentNotifications(@event.UserId); + var userNotifications = await _studentNotificationsRepository.GetByUserIdAsync(@event.UserId) + ?? new UserNotifications(@event.UserId); var emailVerifiedMessage = $"Your email {@event.Email} has been successfully verified!"; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PasswordResetTokenGeneratedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/PasswordResetTokenGeneratedHandler.cs similarity index 90% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PasswordResetTokenGeneratedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/PasswordResetTokenGeneratedHandler.cs index 565fc4753..94c0df29d 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/PasswordResetTokenGeneratedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/PasswordResetTokenGeneratedHandler.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; @@ -7,17 +7,17 @@ using System.Threading; using MiniSpace.Services.Notifications.Application.Services.Clients; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity.Handlers { public class PasswordResetTokenGeneratedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; public PasswordResetTokenGeneratedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient) { _messageBroker = messageBroker; @@ -46,7 +46,7 @@ public async Task HandleAsync(PasswordResetTokenGenerated @event, CancellationTo details: detailsHtml ); - var studentNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(@event.UserId) ?? new StudentNotifications(@event.UserId); + var studentNotifications = await _studentNotificationsRepository.GetByUserIdAsync(@event.UserId) ?? new UserNotifications(@event.UserId); studentNotifications.AddNotification(notification); await _studentNotificationsRepository.AddOrUpdateAsync(studentNotifications); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/SignedUpHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/SignedUpHandler.cs similarity index 90% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/SignedUpHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/SignedUpHandler.cs index 26be61b58..e5fc42302 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/SignedUpHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/SignedUpHandler.cs @@ -2,21 +2,21 @@ using System.Threading; using System.Threading.Tasks; using System.Web; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Core.Repositories; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity.Handlers { public class SignedUpHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; public SignedUpHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository) + IUserNotificationsRepository studentNotificationsRepository) { _messageBroker = messageBroker; _studentNotificationsRepository = studentNotificationsRepository; @@ -24,7 +24,7 @@ public SignedUpHandler( public async Task HandleAsync(SignedUp @event, CancellationToken cancellationToken) { - var userNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(@event.UserId) ?? new StudentNotifications(@event.UserId); + var userNotifications = await _studentNotificationsRepository.GetByUserIdAsync(@event.UserId) ?? new UserNotifications(@event.UserId); var welcomeMessage = $"Welcome to MiniSpace, {@event.FirstName} {@event.LastName}!"; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/TwoFactorCodeGeneratedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/TwoFactorCodeGeneratedHandler.cs similarity index 89% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/TwoFactorCodeGeneratedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/TwoFactorCodeGeneratedHandler.cs index cdae3c6c1..5fcc8abc5 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/TwoFactorCodeGeneratedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/Handlers/TwoFactorCodeGeneratedHandler.cs @@ -1,25 +1,25 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services.Clients; using Microsoft.Extensions.Logging; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity.Handlers { public class TwoFactorCodeGeneratedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly ILogger _logger; public TwoFactorCodeGeneratedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, ILogger logger) { @@ -38,8 +38,8 @@ public async Task HandleAsync(TwoFactorCodeGenerated @event, CancellationToken c return; } - var userNotifications = await _studentNotificationsRepository.GetByStudentIdAsync(@event.UserId) - ?? new StudentNotifications(@event.UserId); + var userNotifications = await _studentNotificationsRepository.GetByUserIdAsync(@event.UserId) + ?? new UserNotifications(@event.UserId); var notificationMessage = $"Your 2FA code is {@event.Code}. Please use this to complete your sign-in."; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PasswordReset.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/PasswordReset.cs similarity index 79% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PasswordReset.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/PasswordReset.cs index 471386718..9f3c6c5fe 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PasswordReset.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/PasswordReset.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity { [Message("identity")] public class PasswordReset : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PasswordResetTokenGenerated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/PasswordResetTokenGenerated.cs similarity index 87% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PasswordResetTokenGenerated.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/PasswordResetTokenGenerated.cs index fef185689..b74809427 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PasswordResetTokenGenerated.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/PasswordResetTokenGenerated.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity { [Message("identity")] public class PasswordResetTokenGenerated : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/SignedUp.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/SignedUp.cs similarity index 91% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/SignedUp.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/SignedUp.cs index c17acad68..56b865c87 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/SignedUp.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/SignedUp.cs @@ -1,8 +1,8 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity { [Message("identity")] public class SignedUp : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/TwoFactorCodeGenerated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/TwoFactorCodeGenerated.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/TwoFactorCodeGenerated.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/TwoFactorCodeGenerated.cs index 9ed48cd77..1349c5d6e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/TwoFactorCodeGenerated.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Identity/TwoFactorCodeGenerated.cs @@ -1,8 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Identity { [Message("identity")] public class TwoFactorCodeGenerated : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PostCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PostCreated.cs deleted file mode 100644 index 6804e4e24..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PostCreated.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; - -namespace MiniSpace.Services.Notifications.Application.Events.External -{ - [Message("posts")] - public class PostCreated : IEvent - { - public Guid PostId { get; } - - public PostCreated(Guid postId) - { - PostId = postId; - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PostUpdated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PostUpdated.cs deleted file mode 100644 index 015168d17..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/PostUpdated.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; - -namespace MiniSpace.Services.Notifications.Application.Events.External -{ - [Message("posts")] - public class PostUpdated : IEvent - { - public Guid PostId { get; } - - public PostUpdated(Guid postId) - { - PostId = postId; - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/Handlers/PostCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/Handlers/PostCreatedHandler.cs new file mode 100644 index 000000000..9fe68db19 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/Handlers/PostCreatedHandler.cs @@ -0,0 +1,147 @@ +using System; +using System.Threading.Tasks; +using System.Threading; +using System.Linq; +using Paralax.CQRS.Events; +using MiniSpace.Services.Notifications.Application.Services.Clients; +using MiniSpace.Services.Notifications.Core.Repositories; +using MiniSpace.Services.Notifications.Core.Entities; +using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Services; +using Microsoft.AspNetCore.SignalR; +using MiniSpace.Services.Notifications.Application.Hubs; +using Microsoft.Extensions.Logging; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Posts.Handlers +{ + public class PostCreatedHandler : IEventHandler + { + private readonly IFriendsServiceClient _friendsServiceClient; + private readonly IOrganizationsServiceClient _organizationsServiceClient; + private readonly IUserNotificationsRepository _userNotificationsRepository; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; + + public PostCreatedHandler( + IFriendsServiceClient friendsServiceClient, + IOrganizationsServiceClient organizationsServiceClient, + IUserNotificationsRepository userNotificationsRepository, + IHubContext hubContext, + ILogger logger) + { + _friendsServiceClient = friendsServiceClient; + _organizationsServiceClient = organizationsServiceClient; + _userNotificationsRepository = userNotificationsRepository; + _hubContext = hubContext; + _logger = logger; + } + + public async Task HandleAsync(PostCreated eventArgs, CancellationToken cancellationToken) + { + if (!eventArgs.ShouldNotify) + { + _logger.LogInformation("PostCreated event received, but notifications are disabled."); + return; + } + + if (eventArgs.UserId.HasValue) + { + await NotifyFriendsAndFollowersAsync(eventArgs); + } + else if (eventArgs.OrganizationId.HasValue) + { + await NotifyOrganizationMembersAsync(eventArgs); + } + } + + private async Task NotifyFriendsAndFollowersAsync(PostCreated eventArgs) + { + try + { + // Get the user's friends + var friends = await _friendsServiceClient.GetFriendsAsync(eventArgs.UserId.Value); + var friendIds = friends.Select(f => f.FriendId).ToList(); + + // Get the user's followers + var followers = await _friendsServiceClient.GetRequestsAsync(eventArgs.UserId.Value); + var followerIds = followers.Select(f => f.InviterId).ToList(); + + // Combine friends and followers into one list of user IDs to notify + var userIdsToNotify = friendIds.Concat(followerIds).Distinct().ToList(); + + foreach (var userId in userIdsToNotify) + { + var notificationMessage = $"A new post has been created by your friend or someone you follow (Post ID: {eventArgs.PostId})."; + await CreateAndSendNotification(userId, eventArgs, notificationMessage); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify user's friends and followers: {ex.Message}"); + } + } + + private async Task NotifyOrganizationMembersAsync(PostCreated eventArgs) + { + try + { + var organizationMembers = await _organizationsServiceClient.GetOrganizationMembersAsync(eventArgs.OrganizationId.Value); + + if (organizationMembers != null && organizationMembers.Users != null) + { + foreach (var member in organizationMembers.Users) + { + var notificationMessage = $"A new post has been created by your organization (Post ID: {eventArgs.PostId})."; + await CreateAndSendNotification(member.Id, eventArgs, notificationMessage); + } + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify organization members: {ex.Message}"); + } + } + + private async Task CreateAndSendNotification(Guid userId, PostCreated eventArgs, string notificationMessage) + { + var detailsHtml = $"

{notificationMessage} Check out the post details here: {eventArgs.TextContent}

"; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: userId, + message: notificationMessage, + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: eventArgs.PostId, + eventType: NotificationEventType.PostCreated, + details: detailsHtml + ); + + _logger.LogInformation($"Creating post creation notification for user: {userId}"); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(userId); + if (userNotifications == null) + { + userNotifications = new UserNotifications(userId); + } + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + var notificationDto = new NotificationDto + { + UserId = userId, + Message = notificationMessage, + CreatedAt = DateTime.UtcNow, + EventType = NotificationEventType.PostCreated, + RelatedEntityId = eventArgs.PostId, + Details = detailsHtml + }; + + // Broadcast SignalR notification + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + _logger.LogInformation($"Broadcasted SignalR notification to user with ID {userId}."); + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/Handlers/PostUpdatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/Handlers/PostUpdatedHandler.cs new file mode 100644 index 000000000..3e55f73dc --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/Handlers/PostUpdatedHandler.cs @@ -0,0 +1,146 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using MiniSpace.Services.Notifications.Application.Services.Clients; +using MiniSpace.Services.Notifications.Core.Repositories; +using MiniSpace.Services.Notifications.Core.Entities; +using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Services; +using Microsoft.AspNetCore.SignalR; +using MiniSpace.Services.Notifications.Application.Hubs; +using Microsoft.Extensions.Logging; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Posts.Handlers +{ + public class PostUpdatedHandler : IEventHandler + { + private readonly IFriendsServiceClient _friendsServiceClient; + private readonly IOrganizationsServiceClient _organizationsServiceClient; + private readonly IUserNotificationsRepository _userNotificationsRepository; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; + + public PostUpdatedHandler( + IFriendsServiceClient friendsServiceClient, + IOrganizationsServiceClient organizationsServiceClient, + IUserNotificationsRepository userNotificationsRepository, + IHubContext hubContext, + ILogger logger) + { + _friendsServiceClient = friendsServiceClient; + _organizationsServiceClient = organizationsServiceClient; + _userNotificationsRepository = userNotificationsRepository; + _hubContext = hubContext; + _logger = logger; + } + + public async Task HandleAsync(PostUpdated eventArgs, CancellationToken cancellationToken) + { + if (!eventArgs.ShouldNotify) + { + _logger.LogInformation("PostUpdated event received, but notifications are disabled."); + return; + } + + if (eventArgs.UserId.HasValue) + { + await NotifyFriendsAndFollowersAsync(eventArgs); + } + else if (eventArgs.OrganizationId.HasValue) + { + await NotifyOrganizationMembersAsync(eventArgs); + } + } + + private async Task NotifyFriendsAndFollowersAsync(PostUpdated eventArgs) + { + try + { + // Get the user's friends + var friends = await _friendsServiceClient.GetFriendsAsync(eventArgs.UserId.Value); + var friendIds = friends.Select(f => f.FriendId).ToList(); + + // Get the user's followers + var followers = await _friendsServiceClient.GetRequestsAsync(eventArgs.UserId.Value); + var followerIds = followers.Select(f => f.InviterId).ToList(); + + // Combine friends and followers into one list of user IDs to notify + var userIdsToNotify = friendIds.Concat(followerIds).Distinct().ToList(); + + foreach (var userId in userIdsToNotify) + { + var notificationMessage = $"An updated post has been created by your friend or someone you follow (Post ID: {eventArgs.PostId})."; + await CreateAndSendNotification(userId, eventArgs, notificationMessage); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify user's friends and followers: {ex.Message}"); + } + } + + private async Task NotifyOrganizationMembersAsync(PostUpdated eventArgs) + { + try + { + var organizationMembers = await _organizationsServiceClient.GetOrganizationMembersAsync(eventArgs.OrganizationId.Value); + + if (organizationMembers != null && organizationMembers.Users != null) + { + foreach (var member in organizationMembers.Users) + { + var notificationMessage = $"An updated post has been created by your organization (Post ID: {eventArgs.PostId})."; + await CreateAndSendNotification(member.Id, eventArgs, notificationMessage); + } + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify organization members: {ex.Message}"); + } + } + + private async Task CreateAndSendNotification(Guid userId, PostUpdated eventArgs, string notificationMessage) + { + var detailsHtml = $"

{notificationMessage} Check out the updated post details here: {eventArgs.TextContent}

"; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: userId, + message: notificationMessage, + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: eventArgs.PostId, + eventType: NotificationEventType.PostUpdated, + details: detailsHtml + ); + + _logger.LogInformation($"Creating post update notification for user: {userId}"); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(userId); + if (userNotifications == null) + { + userNotifications = new UserNotifications(userId); + } + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + var notificationDto = new NotificationDto + { + UserId = userId, + Message = notificationMessage, + CreatedAt = DateTime.UtcNow, + EventType = NotificationEventType.PostUpdated, + RelatedEntityId = eventArgs.PostId, + Details = detailsHtml + }; + + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + _logger.LogInformation($"Broadcasted SignalR notification to user with ID {userId}."); + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/PostCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/PostCreated.cs new file mode 100644 index 000000000..681ca2c83 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/PostCreated.cs @@ -0,0 +1,35 @@ +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using System; +using System.Collections.Generic; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Posts +{ + [Message("posts")] + public class PostCreated : IEvent + { + public Guid PostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public string TextContent { get; } + public IEnumerable MediaFilesUrls { get; } + public string Context { get; } + public string Visibility { get; } + public bool ShouldNotify { get; } + + public PostCreated(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, + IEnumerable mediaFilesUrls, string context, string visibility, bool shouldNotify) + { + PostId = postId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + TextContent = textContent; + MediaFilesUrls = mediaFilesUrls; + Context = context; + Visibility = visibility; + ShouldNotify = shouldNotify; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/PostUpdated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/PostUpdated.cs new file mode 100644 index 000000000..29daf0a1d --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Posts/PostUpdated.cs @@ -0,0 +1,30 @@ +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using System; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Posts +{ + [Message("posts")] + public class PostUpdated : IEvent + { + public Guid PostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public string TextContent { get; } + public string Context { get; } + public bool ShouldNotify { get; } + + public PostUpdated(Guid postId, Guid? userId, Guid? organizationId, + Guid? eventId, string textContent, string context, bool shouldNotify) + { + PostId = postId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + TextContent = textContent; + Context = context; + ShouldNotify = shouldNotify; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReactionCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReactionCreated.cs deleted file mode 100644 index 832b4b93d..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReactionCreated.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Net.Mime; -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Microsoft.AspNetCore.Connections; -using MiniSpace.Services.Notifications.Core.Entities; - -namespace MiniSpace.Services.Notifications.Application.Events.External -{ - [Message("reactions")] - public class ReactionCreated : IEvent - { - public Guid ReactionId {get;set;} - public ReactionCreated(Guid reactionId) - { - ReactionId=reactionId; - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reactions/Handlers/ReactionCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reactions/Handlers/ReactionCreatedHandler.cs new file mode 100644 index 000000000..344337796 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reactions/Handlers/ReactionCreatedHandler.cs @@ -0,0 +1,179 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Notifications.Application.Services.Clients; +using MiniSpace.Services.Notifications.Core.Entities; +using MiniSpace.Services.Notifications.Core.Repositories; +using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Hubs; +using MiniSpace.Services.Notifications.Application.Services; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Reactions.Handlers +{ + public class ReactionCreatedHandler : IEventHandler + { + private readonly IFriendsServiceClient _friendsServiceClient; + private readonly IOrganizationsServiceClient _organizationsServiceClient; + private readonly IPostsServiceClient _postsServiceClient; + private readonly IEventsServiceClient _eventsServiceClient; + private readonly IUserNotificationsRepository _userNotificationsRepository; + private readonly IHubContext _hubContext; + private readonly ILogger _logger; + + public ReactionCreatedHandler( + IFriendsServiceClient friendsServiceClient, + IOrganizationsServiceClient organizationsServiceClient, + IPostsServiceClient postsServiceClient, + IEventsServiceClient eventsServiceClient, + IUserNotificationsRepository userNotificationsRepository, + IHubContext hubContext, + ILogger logger) + { + _friendsServiceClient = friendsServiceClient; + _organizationsServiceClient = organizationsServiceClient; + _postsServiceClient = postsServiceClient; + _eventsServiceClient = eventsServiceClient; + _userNotificationsRepository = userNotificationsRepository; + _hubContext = hubContext; + _logger = logger; + } + + public async Task HandleAsync(ReactionCreated eventArgs, CancellationToken cancellationToken) + { + if (eventArgs.ContentType == "Post" || eventArgs.ContentType == "Event") + { + // Notify the content creator first + await NotifyContentCreatorAsync(eventArgs); + + if (eventArgs.TargetType == "User") + { + await NotifyFriendsAndFollowersAsync(eventArgs); + } + else if (eventArgs.TargetType == "Organization") + { + await NotifyOrganizationMembersAsync(eventArgs); + } + } + } + + private async Task NotifyContentCreatorAsync(ReactionCreated eventArgs) + { + try + { + Guid? contentCreatorId = null; + + // Check the content type and find the content creator + if (eventArgs.ContentType == "Post") + { + var postDetails = await _postsServiceClient.GetPostAsync(eventArgs.ContentId); + contentCreatorId = postDetails?.UserId ?? postDetails?.OrganizationId; + } + else if (eventArgs.ContentType == "Event") + { + var eventDetails = await _eventsServiceClient.GetEventAsync(eventArgs.ContentId); + contentCreatorId = eventDetails?.Organizer.Id ?? eventDetails?.Organizer.OrganizationId; + } + + if (contentCreatorId.HasValue) + { + var notificationMessage = $"Your content has received a new reaction: {eventArgs.ReactionType}."; + await CreateAndSendNotification(contentCreatorId.Value, eventArgs, notificationMessage, isCreator: true); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify content creator: {ex.Message}"); + } + } + + private async Task NotifyFriendsAndFollowersAsync(ReactionCreated eventArgs) + { + try + { + var friends = await _friendsServiceClient.GetFriendsAsync(eventArgs.UserId); + var friendIds = friends.Select(f => f.FriendId).ToList(); + + var followers = await _friendsServiceClient.GetRequestsAsync(eventArgs.UserId); + var followerIds = followers.Select(f => f.InviterId).ToList(); + + var userIdsToNotify = friendIds.Concat(followerIds).Distinct().ToList(); + + foreach (var userId in userIdsToNotify) + { + var notificationMessage = $"A new reaction has been added by your friend or someone you follow (Content ID: {eventArgs.ContentId})."; + await CreateAndSendNotification(userId, eventArgs, notificationMessage); + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify user's friends and followers: {ex.Message}"); + } + } + + private async Task NotifyOrganizationMembersAsync(ReactionCreated eventArgs) + { + try + { + var organizationMembers = await _organizationsServiceClient.GetOrganizationMembersAsync(eventArgs.UserId); + + if (organizationMembers != null && organizationMembers.Users != null) + { + foreach (var member in organizationMembers.Users) + { + var notificationMessage = $"A new reaction has been added to content created by your organization (Content ID: {eventArgs.ContentId})."; + await CreateAndSendNotification(member.Id, eventArgs, notificationMessage); + } + } + } + catch (Exception ex) + { + _logger.LogError($"Failed to notify organization members: {ex.Message}"); + } + } + + private async Task CreateAndSendNotification(Guid userId, ReactionCreated eventArgs, string notificationMessage, bool isCreator = false) + { + var detailsHtml = $"

{notificationMessage} Reaction: {eventArgs.ReactionType} on content.

"; + + var notification = new Notification( + notificationId: Guid.NewGuid(), + userId: userId, + message: notificationMessage, + status: NotificationStatus.Unread, + createdAt: DateTime.UtcNow, + updatedAt: null, + relatedEntityId: eventArgs.ContentId, + eventType: isCreator ? NotificationEventType.ReactionAdded : NotificationEventType.ReactionAdded, + details: detailsHtml + ); + + _logger.LogInformation($"Creating reaction notification for user: {userId}"); + + var userNotifications = await _userNotificationsRepository.GetByUserIdAsync(userId); + if (userNotifications == null) + { + userNotifications = new UserNotifications(userId); + } + + userNotifications.AddNotification(notification); + await _userNotificationsRepository.AddOrUpdateAsync(userNotifications); + + var notificationDto = new NotificationDto + { + UserId = userId, + Message = notificationMessage, + CreatedAt = DateTime.UtcNow, + EventType = isCreator ? NotificationEventType.ReactionAdded : NotificationEventType.ReactionAdded, + RelatedEntityId = eventArgs.ContentId, + Details = detailsHtml + }; + + await NotificationHub.BroadcastNotification(_hubContext, notificationDto, _logger); + _logger.LogInformation($"Broadcasted SignalR notification to user with ID {userId}."); + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reactions/ReactionCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reactions/ReactionCreated.cs new file mode 100644 index 000000000..e1cd67881 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reactions/ReactionCreated.cs @@ -0,0 +1,27 @@ +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using System; + +namespace MiniSpace.Services.Notifications.Application.Events.External.Reactions +{ + [Message("reactions")] + public class ReactionCreated : IEvent + { + public Guid ReactionId { get; } + public Guid UserId { get; } + public Guid ContentId { get; } + public string ReactionType { get; } + public string ContentType { get; } + public string TargetType { get; } + + public ReactionCreated(Guid reactionId, Guid userId, Guid contentId, string reactionType, string contentType, string targetType) + { + ReactionId = reactionId; + UserId = userId; + ContentId = contentId; + ReactionType = reactionType; + ContentType = contentType; + TargetType = targetType; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportCancelledHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportCancelledHandler.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportCancelledHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportCancelledHandler.cs index 6b69e520f..109b68ae3 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportCancelledHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportCancelledHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,19 +11,19 @@ using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Core.Repositories; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports.Handlers { public class ReportCancelledHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IHubContext _hubContext; private readonly ILogger _logger; public ReportCancelledHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, IHubContext hubContext, ILogger logger) @@ -62,7 +62,7 @@ public async Task HandleAsync(ReportCancelled eventArgs, CancellationToken cance private async Task CreateNotificationForUser(Guid userId, ReportCancelled eventArgs, string message) { - var notifications = await _studentNotificationsRepository.GetByStudentIdAsync(userId) ?? new StudentNotifications(userId); + var notifications = await _studentNotificationsRepository.GetByUserIdAsync(userId) ?? new UserNotifications(userId); var notification = new Notification( notificationId: Guid.NewGuid(), userId: userId, diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportCreatedHandler.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportCreatedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportCreatedHandler.cs index 1218d7c85..34bc84864 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportCreatedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportCreatedHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,19 +11,19 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports.Handlers { public class ReportCreatedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IHubContext _hubContext; private readonly ILogger _logger; public ReportCreatedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, IHubContext hubContext, ILogger logger) @@ -63,7 +63,7 @@ public async Task HandleAsync(ReportCreated eventArgs, CancellationToken cancell private async Task CreateNotificationForUser(Guid userId, ReportCreated eventArgs, string message) { - var notifications = await _studentNotificationsRepository.GetByStudentIdAsync(userId) ?? new StudentNotifications(userId); + var notifications = await _studentNotificationsRepository.GetByUserIdAsync(userId) ?? new UserNotifications(userId); var notification = new Notification( notificationId: Guid.NewGuid(), userId: userId, diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportDeletedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportDeletedHandler.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportDeletedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportDeletedHandler.cs index 945e15673..7496d0db2 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportDeletedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportDeletedHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using System.Threading; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,19 +11,19 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports.Handlers { public class ReportDeletedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IHubContext _hubContext; private readonly ILogger _logger; public ReportDeletedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, IHubContext hubContext, ILogger logger) @@ -62,7 +62,7 @@ public async Task HandleAsync(ReportDeleted eventArgs, CancellationToken cancell private async Task CreateNotificationForUser(Guid userId, ReportDeleted eventArgs, string message) { - var notifications = await _studentNotificationsRepository.GetByStudentIdAsync(userId) ?? new StudentNotifications(userId); + var notifications = await _studentNotificationsRepository.GetByUserIdAsync(userId) ?? new UserNotifications(userId); var notification = new Notification( notificationId: Guid.NewGuid(), userId: userId, diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportRejectedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportRejectedHandler.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportRejectedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportRejectedHandler.cs index 05576b0cc..ecfa7dbc3 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportRejectedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportRejectedHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using System.Threading; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,19 +11,19 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports.Handlers { public class ReportRejectedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IHubContext _hubContext; private readonly ILogger _logger; public ReportRejectedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, IHubContext hubContext, ILogger logger) @@ -57,7 +57,7 @@ public async Task HandleAsync(ReportRejected eventArgs, CancellationToken cancel private async Task CreateNotificationForUser(Guid userId, ReportRejected eventArgs, string message) { - var notifications = await _studentNotificationsRepository.GetByStudentIdAsync(userId) ?? new StudentNotifications(userId); + var notifications = await _studentNotificationsRepository.GetByUserIdAsync(userId) ?? new UserNotifications(userId); var notification = new Notification( notificationId: Guid.NewGuid(), userId: userId, diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportResolvedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportResolvedHandler.cs similarity index 93% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportResolvedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportResolvedHandler.cs index 33795b5e0..493ee850f 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportResolvedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportResolvedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Threading.Tasks; using System.Threading; @@ -7,17 +7,17 @@ using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Application.Services.Clients; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports.Handlers { public class ReportResolvedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; public ReportResolvedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient) { _messageBroker = messageBroker; @@ -49,7 +49,7 @@ public async Task HandleAsync(ReportResolved eventArgs, CancellationToken cancel private async Task CreateNotificationForUser(Guid userId, ReportResolved eventArgs, string message) { - var notifications = await _studentNotificationsRepository.GetByStudentIdAsync(userId) ?? new StudentNotifications(userId); + var notifications = await _studentNotificationsRepository.GetByUserIdAsync(userId) ?? new UserNotifications(userId); var notification = new Notification( notificationId: Guid.NewGuid(), userId: userId, diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportReviewStartedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportReviewStartedHandler.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportReviewStartedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportReviewStartedHandler.cs index 9fac1b165..609f3c438 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/ReportReviewStartedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/Handlers/ReportReviewStartedHandler.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using System.Threading; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Dto; @@ -11,19 +11,19 @@ using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; -namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports.Handlers { public class ReportReviewStartedHandler : IEventHandler { private readonly IMessageBroker _messageBroker; - private readonly IStudentNotificationsRepository _studentNotificationsRepository; + private readonly IUserNotificationsRepository _studentNotificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; private readonly IHubContext _hubContext; private readonly ILogger _logger; public ReportReviewStartedHandler( IMessageBroker messageBroker, - IStudentNotificationsRepository studentNotificationsRepository, + IUserNotificationsRepository studentNotificationsRepository, IStudentsServiceClient studentsServiceClient, IHubContext hubContext, ILogger logger) @@ -56,7 +56,7 @@ public async Task HandleAsync(ReportReviewStarted eventArgs, CancellationToken c private async Task CreateNotificationForUser(Guid userId, ReportReviewStarted eventArgs, string message) { - var notifications = await _studentNotificationsRepository.GetByStudentIdAsync(userId) ?? new StudentNotifications(userId); + var notifications = await _studentNotificationsRepository.GetByUserIdAsync(userId) ?? new UserNotifications(userId); var notification = new Notification( notificationId: Guid.NewGuid(), userId: userId, diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportCancelled.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportCancelled.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportCancelled.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportCancelled.cs index 616ff61fe..76fa62bf0 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportCancelled.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportCancelled.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports { [Message("reports")] public class ReportCancelled : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportCreated.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportCreated.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportCreated.cs index dab7a0b61..83037b95b 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportCreated.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportCreated.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports { [Message("reports")] public class ReportCreated: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportDeleted.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportDeleted.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportDeleted.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportDeleted.cs index 883b63a75..3b6e35dc8 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportDeleted.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportDeleted.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports { [Message("reports")] public class ReportDeleted: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportRejected.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportRejected.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportRejected.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportRejected.cs index 610bfd4e8..23eb5ae18 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportRejected.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportRejected.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports { [Message("reports")] public class ReportRejected: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportResolved.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportResolved.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportResolved.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportResolved.cs index b71848338..ea555042d 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportResolved.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportResolved.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports { [Message("reports")] public class ReportResolved: IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportReviewStarted.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportReviewStarted.cs similarity index 94% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportReviewStarted.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportReviewStarted.cs index 6e7a3394f..488249904 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/ReportReviewStarted.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Reports/ReportReviewStarted.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; -namespace MiniSpace.Services.Notifications.Application.Events.External +namespace MiniSpace.Services.Notifications.Application.Events.External.Reports { [Message("reports")] public class ReportReviewStarted : IEvent diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationCreatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationCreatedHandler.cs similarity index 88% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationCreatedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationCreatedHandler.cs index c52152a5a..1bb597424 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationCreatedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationCreatedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core.Entities; @@ -10,12 +10,12 @@ namespace MiniSpace.Services.Notifications.Application.Events.External.Handlers { public class NotificationCreatedHandler : IEventHandler { - private readonly IStudentNotificationsRepository _notificationRepository; + private readonly IUserNotificationsRepository _notificationRepository; private readonly IEventMapper _eventMapper; private readonly IMessageBroker _messageBroker; public NotificationCreatedHandler( - IStudentNotificationsRepository notificationRepository, + IUserNotificationsRepository notificationRepository, IEventMapper eventMapper, IMessageBroker messageBroker) { @@ -26,7 +26,7 @@ public NotificationCreatedHandler( public async Task HandleAsync(NotificationCreated @event, CancellationToken cancellationToken) { - // var notification = await _notificationRepository.GetByStudentIdAsync(@event.UserId); + // var notification = await _notificationRepository.GetByUserIdAsync(@event.UserId); // // GetAsync(@event.NotificationId); // if (notification == null) // { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationDeletedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationDeletedHandler.cs similarity index 98% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationDeletedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationDeletedHandler.cs index 8ccbaff4d..0e4bf9b6e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationDeletedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationDeletedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Application.Services; using System.Threading.Tasks; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationUpdatedHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationUpdatedHandler.cs similarity index 98% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationUpdatedHandler.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationUpdatedHandler.cs index 5d65cca11..2becb0bf9 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/Handlers/NotificationUpdatedHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Handlers/NotificationUpdatedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Application.Exceptions; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationCreated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationCreated.cs similarity index 97% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationCreated.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationCreated.cs index 3acb63f0a..ca53a3ae1 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationCreated.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Notifications.Application.Events.External { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationDeleted.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationDeleted.cs similarity index 84% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationDeleted.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationDeleted.cs index d10515dc3..36a5f757a 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationDeleted.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationDeleted.cs @@ -1,9 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Notifications.Application.Events.External { - [Contract] public class NotificationDeleted : IEvent { public Guid UserId { get; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationUpdated.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationUpdated.cs similarity index 88% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationUpdated.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationUpdated.cs index a5145a1e3..d37d92bb7 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/External/NotificationUpdated.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/NotificationUpdated.cs @@ -1,10 +1,9 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using MiniSpace.Services.Notifications.Core.Entities; namespace MiniSpace.Services.Notifications.Application.Events.External { - [Contract] public class NotificationUpdated : IEvent { public Guid NotificationId { get; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationCreationRejected.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationCreationRejected.cs index fb1249a76..ba883684f 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationCreationRejected.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationCreationRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Notifications.Application.Events.Rejected { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationDeletionRejected.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationDeletionRejected.cs index 91946de43..a3b4079de 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationDeletionRejected.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationDeletionRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Notifications.Application.Events.Rejected { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationProcessRejected.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationProcessRejected.cs index 1cbfaac25..c65afa45a 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationProcessRejected.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationProcessRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Notifications.Application.Events.Rejected { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationUpdateRejected.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationUpdateRejected.cs index 4ca74c884..7592ed8ae 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationUpdateRejected.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Events/Rejected/NotificationUpdateRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Notifications.Application.Events.Rejected { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Extensions.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Extensions.cs index 893e20fa4..8c39d5e13 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Extensions.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Extensions.cs @@ -1,12 +1,12 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Notifications.Application { public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/MiniSpace.Services.Notifications.Application.csproj b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/MiniSpace.Services.Notifications.Application.csproj index 95b3cf275..7a8c1c314 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/MiniSpace.Services.Notifications.Application.csproj +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/MiniSpace.Services.Notifications.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotification.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotification.cs index 7470403d4..4e5e9b97c 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotification.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotification.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Notifications.Application.Dto; using System; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotificationsByUser.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotificationsByUser.cs index e1b100a1b..f74b390d2 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotificationsByUser.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/GetNotificationsByUser.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Core.Entities; using System; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedNotificationsQuery.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedNotificationsQuery.cs index bea46741f..d43987956 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedNotificationsQuery.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedNotificationsQuery.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using System.Collections.Generic; namespace MiniSpace.Services.Notifications.Application.Queries diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedQuery.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedQuery.cs index adc18ac8e..b7d200750 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedQuery.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/IPagedQuery.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; namespace MiniSpace.Services.Notifications.Application.Queries { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/PagedResult.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/PagedResult.cs index 5b8bbbf48..78183377d 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/PagedResult.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Queries/PagedResult.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; namespace MiniSpace.Services.Students.Application.Queries { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/ICommentsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/ICommentsServiceClient.cs index 0a07ac280..df0f307d6 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/ICommentsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/ICommentsServiceClient.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Dto.Comments; namespace MiniSpace.Services.Notifications.Application.Services.Clients { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IEventsServiceClinet.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IEventsServiceClinet.cs index cb8c41037..f026320bf 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IEventsServiceClinet.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IEventsServiceClinet.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Dto.Events; namespace MiniSpace.Services.Notifications.Application.Services.Clients { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IOrganizationsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IOrganizationsServiceClient.cs new file mode 100644 index 000000000..a4ff52ac3 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IOrganizationsServiceClient.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; +using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Dto.Organizations; + +namespace MiniSpace.Services.Notifications.Application.Services.Clients +{ + public interface IOrganizationsServiceClient + { + Task GetOrganizationAsync(Guid organizationId); + Task GetOrganizationMembersAsync(Guid organizationId); + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IPostsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IPostsServiceClient.cs index 0515f9857..6c541b145 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IPostsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IPostsServiceClient.cs @@ -1,4 +1,4 @@ -using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Dto.Posts; namespace MiniSpace.Services.Notifications.Application.Services.Clients { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IStudentsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IStudentsServiceClient.cs index 7f743914c..149cced2e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IStudentsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/Clients/IStudentsServiceClient.cs @@ -6,8 +6,8 @@ namespace MiniSpace.Services.Notifications.Application.Services.Clients { public interface IStudentsServiceClient { - Task GetAsync(Guid id); - public Task> GetAllAsync(); + Task GetAsync(Guid id); + public Task> GetAllAsync(); } } \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IBaseUrlService.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IBaseUrlService.cs new file mode 100644 index 000000000..72d203ee0 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IBaseUrlService.cs @@ -0,0 +1,7 @@ +namespace MiniSpace.Services.Notifications.Application.Services +{ + public interface IBaseUrlService + { + string GetBaseUrl(); + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IEventMapper.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IEventMapper.cs index 73e38bb40..b446605c4 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Core.Events; namespace MiniSpace.Services.Notifications.Application.Services diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IMessageBroker.cs index 894dce62c..94cdb0bc5 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Notifications.Application.Services { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/FriendEvent.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/FriendEvent.cs index d480b00a2..79aa0efd8 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/FriendEvent.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/FriendEvent.cs @@ -10,7 +10,7 @@ public class FriendEvent public string Details { get; private set; } public DateTime CreatedAt { get; private set; } - public FriendEvent(Guid id, Guid eventId, Guid userId, Guid friendId, string eventType, string details, DateTime createdAt) + public FriendEvent(Guid id, Guid eventId, Guid userId, Guid friendId, string eventType, string details, DateTime createdAt) { Id = id; EventId = eventId; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/Message.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/Message.cs deleted file mode 100644 index 40f02e896..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/Message.cs +++ /dev/null @@ -1,23 +0,0 @@ - -namespace MiniSpace.Services.Notifications.Core.Entities -{ - public class Message - { - public Guid Id { get; set; } - public string Sender { get; set; } - public string Receiver { get; set; } // can be a user or a group name - public string Content { get; set; } - public DateTime Timestamp { get; set; } - public MessageType Type { get; set; } - - public Message(string sender, string receiver, string content, MessageType type) - { - Id = Guid.NewGuid(); - Sender = sender; - Receiver = receiver; - Content = content; - Timestamp = DateTime.UtcNow; - Type = type; - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/NotificationEventType.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/NotificationEventType.cs index 51cae3f8e..5fde6b8b4 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/NotificationEventType.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/NotificationEventType.cs @@ -32,5 +32,6 @@ public enum NotificationEventType Other, EmailVerified, TwoFactorCodeGenerated, + CommentLikeAdded, } } \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/Student.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/User.cs similarity index 80% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/Student.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/User.cs index 4cfdd199a..2fcb79c71 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/Student.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/User.cs @@ -3,7 +3,7 @@ namespace MiniSpace.Services.Notifications.Core.Entities { - public class Student + public class User { public Guid Id { get; set; } public string Email { get; set; } @@ -21,7 +21,10 @@ public class Student public List InterestedInEvents { get; set; } public List SignedUpEvents { get; set; } - public Student(Guid id, string email, string firstName, string lastName, int numberOfFriends, Guid profileImage, string description, DateTime dateOfBirth, bool emailNotifications, bool isBanned, bool isOrganizer, string state, DateTime createdAt) + public User(Guid id, string email, string firstName, string lastName, + int numberOfFriends, Guid profileImage, string description, + DateTime dateOfBirth, bool emailNotifications, bool isBanned, + bool isOrganizer, string state, DateTime createdAt) { Id = id; Email = email; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/StudentNotifications.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/UserNotifications.cs similarity index 85% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/StudentNotifications.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/UserNotifications.cs index 81f2f05cc..47a80d961 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/StudentNotifications.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Entities/UserNotifications.cs @@ -4,16 +4,16 @@ namespace MiniSpace.Services.Notifications.Core.Entities { - public class StudentNotifications + public class UserNotifications { - public Guid StudentId { get; private set; } + public Guid UserId { get; private set; } private List _notifications; public IEnumerable Notifications => _notifications.AsReadOnly(); - public StudentNotifications(Guid studentId) + public UserNotifications(Guid userId) { - StudentId = studentId; + UserId = userId; _notifications = new List(); } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IStudentNotificationsRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IStudentNotificationsRepository.cs deleted file mode 100644 index 07192644a..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IStudentNotificationsRepository.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using MiniSpace.Services.Notifications.Core.Entities; - -namespace MiniSpace.Services.Notifications.Core.Repositories -{ - public interface IStudentNotificationsRepository - { - Task GetByStudentIdAsync(Guid studentId); - Task AddAsync(StudentNotifications studentNotifications); - Task UpdateAsync(StudentNotifications studentNotifications); - Task AddOrUpdateAsync(StudentNotifications studentNotifications); - Task DeleteAsync(Guid studentId); - Task UpdateNotificationStatus(Guid studentId, Guid notificationId, string newStatus); - Task NotificationExists(Guid studentId, Guid notificationId); - Task DeleteNotification(Guid studentId, Guid notificationId); - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IStudentRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IStudentRepository.cs deleted file mode 100644 index fdb050ba5..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IStudentRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using MiniSpace.Services.Notifications.Core.Entities; - -namespace MiniSpace.Services.Notifications.Core.Repositories -{ - public interface IStudentRepository - { - Task GetAsync(Guid id); - Task> GetAllAsync(); - Task AddAsync(Student student); - Task UpdateAsync(Student student); - Task DeleteAsync(Guid id); - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IUserNotificationsRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IUserNotificationsRepository.cs new file mode 100644 index 000000000..c0b1c9d21 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Core/Repositories/IUserNotificationsRepository.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MiniSpace.Services.Notifications.Core.Entities; + +namespace MiniSpace.Services.Notifications.Core.Repositories +{ + public interface IUserNotificationsRepository + { + Task GetByUserIdAsync(Guid userId); + Task AddAsync(UserNotifications userNotifications); + Task UpdateAsync(UserNotifications userNotifications); + Task AddOrUpdateAsync(UserNotifications userNotifications); + Task DeleteAsync(Guid userId); + Task UpdateNotificationStatus(Guid userId, Guid notificationId, string newStatus); + Task NotificationExists(Guid userId, Guid notificationId); + Task DeleteNotification(Guid userId, Guid notificationId); + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Contexts/AppContextFactory.cs index 770b5d3fe..13c28a8da 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Notifications.Application; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 704245c73..047b08f0e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Notifications.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 35d507839..a31ab3840 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Notifications.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index e191642f7..71fa348df 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Notifications.Application.Commands; using MiniSpace.Services.Notifications.Application.Events.Rejected; using MiniSpace.Services.Notifications.Application.Exceptions; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index cf7aeda26..c3b8a7bf9 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Notifications.Application.Exceptions; using MiniSpace.Services.Notifications.Core.Exceptions; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Extensions.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Extensions.cs index eb078bb71..236dc778b 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -43,22 +43,28 @@ using MiniSpace.Services.Notifications.Infrastructure.Services.Clients; // using MiniSpace.Services.Notifications.Infrastructure.Managers; using MiniSpace.Services.Notifications.Application.Hubs; +using MiniSpace.Services.Notifications.Application.Events.External.Comments; +using MiniSpace.Services.Notifications.Application.Events.External.Identity; +using MiniSpace.Services.Notifications.Application.Events.External.Reports; +using MiniSpace.Services.Notifications.Application.Events.External.Reactions; +using MiniSpace.Services.Notifications.Application.Events.External.Posts; +using MiniSpace.Services.Notifications.Application.Events.External.Friends; +using MiniSpace.Services.Notifications.Application.Events.External.Events; namespace MiniSpace.Services.Notifications.Infrastructure { public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddSingleton(); builder.Services.AddSingleton(); // builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddTransient(); - builder.Services.AddSingleton(); + builder.Services.AddTransient(); + builder.Services.AddTransient(); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -93,8 +99,7 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) .AddHandlersLogging() .AddMongoRepository("notifications") .AddMongoRepository("friend-service") - .AddMongoRepository("students") - .AddMongoRepository("students-notifications") + .AddMongoRepository("user_notifications") // .AddMongoRepository("events-service") .AddSignalRInfrastructure() .AddWebApiSwaggerDocs() @@ -107,7 +112,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() @@ -147,7 +152,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app return app; } - public static IConveyBuilder AddSignalRInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddSignalRInfrastructure(this IParalaxBuilder builder) { builder.Services.AddCors(options => { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/IExtendedStudentNotificationsRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/IExtendedStudentNotificationsRepository.cs index 092c815c2..dd8eb7e5a 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/IExtendedStudentNotificationsRepository.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/IExtendedStudentNotificationsRepository.cs @@ -5,9 +5,10 @@ namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Repositories { - public interface IExtendedStudentNotificationsRepository : IStudentNotificationsRepository + public interface IExtendedUserNotificationsRepository : IUserNotificationsRepository { - Task BulkUpdateAsync(FilterDefinition filter, UpdateDefinition update); + Task BulkUpdateAsync(FilterDefinition filter, + UpdateDefinition update); Task GetNotificationCount(Guid studentId); Task> GetRecentNotifications(Guid studentId, int count); } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/Extensions.cs index e0eaa1120..4d6e6cbf7 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/Extensions.cs @@ -1,5 +1,5 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; +using Paralax.CQRS.Logging; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Notifications.Application.Commands; @@ -7,7 +7,7 @@ namespace MiniSpace.Services.Notifications.Infrastructure.Logging { internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(UpdateNotificationStatus).Assembly; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 699930032..413134b2b 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,7 +1,8 @@ -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using Microsoft.Extensions.Logging; using MiniSpace.Services.Notifications.Application.Commands; using MiniSpace.Services.Notifications.Application.Events.External; +using MiniSpace.Services.Notifications.Application.Events.External.Friends; namespace MiniSpace.Services.Notifications.Infrastructure.Logging { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/MiniSpace.Services.Notifications.Infrastructure.csproj b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/MiniSpace.Services.Notifications.Infrastructure.csproj index aae3a8d6c..c84fb03da 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/MiniSpace.Services.Notifications.Infrastructure.csproj +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/MiniSpace.Services.Notifications.Infrastructure.csproj @@ -7,32 +7,37 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/Extensions.cs index 39a3dff69..79fe59428 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/Extensions.cs @@ -101,69 +101,9 @@ public static FriendEventDto AsDto(this FriendEventDocument document) }; } - public static Student AsEntity(this StudentDocument document) + public static UserNotifications AsEntity(this UserNotificationsDocument document) { - return new Student( - document.Id, - document.Email, - document.FirstName, - document.LastName, - document.NumberOfFriends, - document.ProfileImage, - document.Description, - document.DateOfBirth, - document.EmailNotifications, - document.IsBanned, - document.IsOrganizer, - document.State, - document.CreatedAt - ); - } - - public static StudentDocument AsDocument(this Student entity) - { - return new StudentDocument - { - Id = entity.Id, - Email = entity.Email, - FirstName = entity.FirstName, - LastName = entity.LastName, - NumberOfFriends = entity.NumberOfFriends, - ProfileImage = entity.ProfileImage, - Description = entity.Description, - DateOfBirth = entity.DateOfBirth, - EmailNotifications = entity.EmailNotifications, - IsBanned = entity.IsBanned, - IsOrganizer = entity.IsOrganizer, - State = entity.State, - CreatedAt = entity.CreatedAt, - InterestedInEvents = entity.InterestedInEvents, - SignedUpEvents = entity.SignedUpEvents - }; - } - - public static StudentDto AsDto(this StudentDocument document) - { - return new StudentDto - { - Id = document.Id, - Email = document.Email, - FirstName = document.FirstName, - LastName = document.LastName, - Description = document.Description, - DateOfBirth = document.DateOfBirth, - EmailNotifications = document.EmailNotifications, - IsBanned = document.IsBanned, - State = document.State, - CreatedAt = document.CreatedAt, - InterestedInEvents = document.InterestedInEvents, - SignedUpEvents = document.SignedUpEvents - }; - } - - public static StudentNotifications AsEntity(this StudentNotificationsDocument document) - { - var studentNotifications = new StudentNotifications(document.StudentId); + var studentNotifications = new UserNotifications(document.UserId); foreach (var notificationDocument in document.Notifications) { var notification = notificationDocument.AsEntity(); @@ -172,7 +112,7 @@ public static StudentNotifications AsEntity(this StudentNotificationsDocument do return studentNotifications; } - public static StudentNotificationsDocument AsDocument(this StudentNotifications entity) + public static UserNotificationsDocument AsDocument(this UserNotifications entity) { var notifications = new List(); foreach (var notification in entity.Notifications) @@ -180,15 +120,15 @@ public static StudentNotificationsDocument AsDocument(this StudentNotifications notifications.Add(notification.AsDocument()); } - return new StudentNotificationsDocument + return new UserNotificationsDocument { Id = Guid.NewGuid(), - StudentId = entity.StudentId, + UserId = entity.UserId, Notifications = notifications }; } - public static IEnumerable AsDto(this StudentNotificationsDocument document) + public static IEnumerable AsDto(this UserNotificationsDocument document) { return document.Notifications.Select(nd => nd.AsDto()); } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/FriendEventDocument.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/FriendEventDocument.cs index befdb7b87..ac99786eb 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/FriendEventDocument.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/FriendEventDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/NotificationDocument.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/NotificationDocument.cs index a201f4040..c94048646 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/NotificationDocument.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/NotificationDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Notifications.Core.Entities; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/StudentDocument.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/StudentDocument.cs deleted file mode 100644 index 08d9dcace..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/StudentDocument.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Convey.Types; - -namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents -{ - public class StudentDocument : IIdentifiable - { - public Guid Id { get; set; } - public string Email { get; set; } - public string FirstName { get; set; } - public string LastName { get; set; } - public int NumberOfFriends { get; set; } - public Guid ProfileImage { get; set; } - public string Description { get; set; } - public DateTime DateOfBirth { get; set; } - public bool EmailNotifications { get; set; } - public bool IsBanned { get; set; } - public bool IsOrganizer { get; set; } - public string State { get; set; } - public DateTime CreatedAt { get; set; } - public List InterestedInEvents { get; set; } - public List SignedUpEvents { get; set; } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/StudentNotificationsDocument.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/UserNotificationsDocument.cs similarity index 73% rename from MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/StudentNotificationsDocument.cs rename to MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/UserNotificationsDocument.cs index 75e5f07b3..0657fa7ff 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/StudentNotificationsDocument.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Documents/UserNotificationsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; using System; @@ -6,12 +6,12 @@ namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents { - public class StudentNotificationsDocument : IIdentifiable + public class UserNotificationsDocument : IIdentifiable { [BsonId] [BsonRepresentation(BsonType.String)] public Guid Id { get; set; } - public Guid StudentId { get; set; } + public Guid UserId { get; set; } public List Notifications { get; set; } } } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationHandler.cs index f23ecac72..c7a58a506 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Exceptions; using MiniSpace.Services.Notifications.Application.Queries; @@ -13,16 +13,16 @@ namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Queries.Handlers { public class GetNotificationHandler : IQueryHandler { - private readonly IMongoRepository _repository; + private readonly IMongoRepository _repository; - public GetNotificationHandler(IMongoRepository repository) + public GetNotificationHandler(IMongoRepository repository) { _repository = repository; } public async Task HandleAsync(GetNotification query, CancellationToken cancellationToken) { - var document = await _repository.GetAsync(d => d.StudentId == query.UserId); + var document = await _repository.GetAsync(d => d.UserId == query.UserId); if (document == null) { diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationsByUserHandler.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationsByUserHandler.cs index 881a72041..bf86fcaaa 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationsByUserHandler.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Queries/Handlers/GetNotificationsByUserHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Queries; using MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents; @@ -14,34 +14,34 @@ namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Queries.Handlers { public class GetNotificationsByUserHandler : IQueryHandler> { - private readonly IMongoRepository _repository; + private readonly IMongoRepository _repository; private const string BaseUrl = "notifications"; - public GetNotificationsByUserHandler(IMongoRepository repository) + public GetNotificationsByUserHandler(IMongoRepository repository) { _repository = repository; } public async Task> HandleAsync(GetNotificationsByUser query, CancellationToken cancellationToken) { - var filter = Builders.Filter.Eq(doc => doc.StudentId, query.UserId); + var filter = Builders.Filter.Eq(doc => doc.UserId, query.UserId); if (!string.IsNullOrEmpty(query.Status)) { var regex = new MongoDB.Bson.BsonRegularExpression($"^{Regex.Escape(query.Status)}$", "i"); - var statusFilter = Builders.Filter.ElemMatch(x => x.Notifications, + var statusFilter = Builders.Filter.ElemMatch(x => x.Notifications, Builders.Filter.Regex("Status", regex)); - filter = Builders.Filter.And(filter, statusFilter); + filter = Builders.Filter.And(filter, statusFilter); // Console.WriteLine($"Applying status filter with regex: {regex}"); } - var sortBy = Builders.Sort.Descending(doc => doc.Notifications[-1].CreatedAt); + var sortBy = Builders.Sort.Descending(doc => doc.Notifications[-1].CreatedAt); if (query.SortOrder.ToLower() == "asc") { - sortBy = Builders.Sort.Ascending(doc => doc.Notifications[-1].CreatedAt); + sortBy = Builders.Sort.Ascending(doc => doc.Notifications[-1].CreatedAt); } var notificationsList = new List(); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/FriendEventMongoRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/FriendEventMongoRepository.cs index efa65c158..6d20c548e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/FriendEventMongoRepository.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/FriendEventMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents; using MiniSpace.Services.Notifications.Core.Repositories; using MongoDB.Driver; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/NotificationMongoRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/NotificationMongoRepository.cs index 956c6ba9f..3faffd0d1 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/NotificationMongoRepository.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/NotificationMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Notifications.Core.Entities; using MiniSpace.Services.Notifications.Core.Repositories; using MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs deleted file mode 100644 index 9cdcf95e0..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Notifications.Core.Entities; -using MiniSpace.Services.Notifications.Core.Repositories; -using MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents; -using MongoDB.Driver; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Repositories -{ - public class StudentMongoRepository : IStudentRepository - { - private readonly IMongoRepository _repository; - - public StudentMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - - public async Task GetAsync(Guid id) - { - var document = await _repository.GetAsync(o => o.Id == id); - return document?.AsEntity(); - } - - public async Task> GetAllAsync() - { - var documents = await _repository.FindAsync(_ => true); - List students = new List(); - foreach (var doc in documents) - { - students.Add(doc.AsEntity()); - } - return students; - } - - public Task AddAsync(Student student) - => _repository.AddAsync(student.AsDocument()); - - public async Task UpdateAsync(Student student) - { - var filter = Builders.Filter.Eq(doc => doc.Id, student.Id); - var update = Builders.Update - .Set(doc => doc.FirstName, student.FirstName) - .Set(doc => doc.LastName, student.LastName) - .Set(doc => doc.ProfileImage, student.ProfileImage) - // Ensure to update other fields as necessary - .Set(doc => doc.Email, student.Email) - .Set(doc => doc.Description, student.Description) - .Set(doc => doc.DateOfBirth, student.DateOfBirth) - .Set(doc => doc.State, student.State); - - await _repository.Collection.UpdateOneAsync(filter, update); - } - - - public Task DeleteAsync(Guid id) - => _repository.DeleteAsync(id); - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/StudentNotificationsMongoRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/StudentNotificationsMongoRepository.cs deleted file mode 100644 index d6c04c05c..000000000 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/StudentNotificationsMongoRepository.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Notifications.Core.Entities; -using MiniSpace.Services.Notifications.Core.Repositories; -using MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents; -using MongoDB.Driver; - -namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Repositories -{ - public class StudentNotificationsMongoRepository : IExtendedStudentNotificationsRepository - { - private readonly IMongoRepository _repository; - - public StudentNotificationsMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - - public async Task GetByStudentIdAsync(Guid studentId) - { - var document = await _repository.GetAsync(d => d.StudentId == studentId); - return document?.AsEntity(); - } - - public async Task AddAsync(StudentNotifications studentNotifications) - { - var document = studentNotifications.AsDocument(); - await _repository.AddAsync(document); - } - - public async Task UpdateAsync(StudentNotifications studentNotifications) - { - var filter = Builders.Filter.Eq(doc => doc.StudentId, studentNotifications.StudentId); - var update = Builders.Update - .SetOnInsert(doc => doc.Id, studentNotifications.StudentId) - .PushEach(doc => doc.Notifications, studentNotifications.Notifications.Select(n => n.AsDocument())); - - var options = new UpdateOptions { IsUpsert = true }; - await _repository.Collection.UpdateOneAsync(filter, update, options); - } - - // public async Task AddOrUpdateAsync(StudentNotifications studentNotifications) - // { - // var document = studentNotifications.AsDocument(); - // var filter = Builders.Filter.Eq(doc => doc.StudentId, studentNotifications.StudentId); - // var updates = new List>(); - - // foreach (var notification in studentNotifications.Notifications) - // { - // updates.Add(Builders.Update.Push(doc => doc.Notifications, notification.AsDocument())); - // } - - // var update = Builders.Update - // .SetOnInsert(doc => doc.Id, studentNotifications.StudentId) - // .AddToSetEach(doc => doc.Notifications, studentNotifications.Notifications.Select(n => n.AsDocument())); - - // var options = new UpdateOptions { IsUpsert = true }; - // await _repository.Collection.UpdateOneAsync(filter, update, options); - // } - - public async Task AddOrUpdateAsync(StudentNotifications studentNotifications) - { - var document = studentNotifications.AsDocument(); - var filter = Builders.Filter.Eq(doc => doc.StudentId, studentNotifications.StudentId); - - var initializationUpdate = Builders.Update - .SetOnInsert(doc => doc.Notifications, new List()) - .SetOnInsert(doc => doc.Id, document.Id); - - var addToSetUpdates = new List>(); - foreach (var notification in studentNotifications.Notifications) - { - var notificationDocument = notification.AsDocument(); - var notificationFilter = Builders.Filter.And( - Builders.Filter.Eq("Notifications.Message", notificationDocument.Message), - Builders.Filter.Eq("Notifications.CreatedAt", notificationDocument.CreatedAt), - Builders.Filter.Eq("Notifications.EventType", notificationDocument.EventType) - ); - - var combinedFilter = Builders.Filter.And(filter, notificationFilter); - var update = Builders.Update.AddToSet(doc => doc.Notifications, notificationDocument); - - addToSetUpdates.Add(update); - } - - var options = new UpdateOptions { IsUpsert = true }; - - await _repository.Collection.UpdateOneAsync(filter, initializationUpdate, options); - - foreach (var update in addToSetUpdates) - { - await _repository.Collection.UpdateOneAsync(filter, update, options); - } - } - - - - - public async Task> FindAsync(FilterDefinition filter, FindOptions options) - { - var documents = await _repository.Collection.Find(filter, options).ToListAsync(); - return documents.Select(doc => doc.AsEntity()).ToList(); - } - - - public Task DeleteAsync(Guid studentId) - { - return _repository.DeleteAsync(studentId); - } - - public async Task BulkUpdateAsync(FilterDefinition filter, UpdateDefinition update) - { - return await _repository.Collection.UpdateManyAsync(filter, update); - } - - public async Task UpdateNotificationStatus(Guid studentId, Guid notificationId, string newStatus) - { - var filter = Builders.Filter.And( - Builders.Filter.Eq(doc => doc.StudentId, studentId), - Builders.Filter.ElemMatch(doc => doc.Notifications, n => n.NotificationId == notificationId) - ); - - var update = Builders.Update - .Set("Notifications.$.Status", newStatus) - .CurrentDate("Notifications.$.UpdatedAt"); - - await _repository.Collection.UpdateOneAsync(filter, update); - } - - public async Task NotificationExists(Guid studentId, Guid notificationId) - { - var filter = Builders.Filter.And( - Builders.Filter.Eq(doc => doc.StudentId, studentId), - Builders.Filter.ElemMatch(doc => doc.Notifications, n => n.NotificationId == notificationId) - ); - - var result = await _repository.Collection.Find(filter).AnyAsync(); - return result; - } - - public async Task DeleteNotification(Guid studentId, Guid notificationId) - { - var filter = Builders.Filter.And( - Builders.Filter.Eq(d => d.StudentId, studentId), - Builders.Filter.ElemMatch(e => e.Notifications, n => n.NotificationId == notificationId)); - - var update = Builders.Update.PullFilter( - p => p.Notifications, n => n.NotificationId == notificationId); - - await _repository.Collection.UpdateOneAsync(filter, update); - } - - public async Task GetNotificationCount(Guid studentId) - { - var studentNotifications = await _repository.GetAsync(x => x.StudentId == studentId); - return studentNotifications?.Notifications.Count ?? 0; - } - - public async Task> GetRecentNotifications(Guid studentId, int count) - { - var filter = Builders.Filter.Eq(d => d.StudentId, studentId); - - var options = new FindOptions - { - Sort = Builders.Sort.Descending(d => d.Notifications[-1].CreatedAt), - Projection = Builders.Projection.Slice(p => p.Notifications, -count) - }; - - var cursor = await _repository.Collection.FindAsync(filter, options); - - var documents = await cursor.ToListAsync(); - var notifications = new List(); - foreach (var document in documents) - { - if (document.Notifications != null) - { - notifications.AddRange(document.Notifications); - } - } - - return notifications; - } - } -} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/UserNotificationsMongoRepository.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/UserNotificationsMongoRepository.cs new file mode 100644 index 000000000..e75fb75b6 --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Mongo/Repositories/UserNotificationsMongoRepository.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Paralax.Persistence.MongoDB; +using MiniSpace.Services.Notifications.Core.Entities; +using MiniSpace.Services.Notifications.Core.Repositories; +using MiniSpace.Services.Notifications.Infrastructure.Mongo.Documents; +using MongoDB.Driver; + +namespace MiniSpace.Services.Notifications.Infrastructure.Mongo.Repositories +{ + public class UserNotificationsMongoRepository : IExtendedUserNotificationsRepository + { + private readonly IMongoRepository _repository; + + public UserNotificationsMongoRepository(IMongoRepository repository) + { + _repository = repository; + } + + public async Task GetByUserIdAsync(Guid studentId) + { + var document = await _repository.GetAsync(d => d.UserId == studentId); + return document?.AsEntity(); + } + + public async Task AddAsync(UserNotifications studentNotifications) + { + var document = studentNotifications.AsDocument(); + await _repository.AddAsync(document); + } + + public async Task UpdateAsync(UserNotifications studentNotifications) + { + var filter = Builders.Filter.Eq(doc => doc.UserId, studentNotifications.UserId); + var update = Builders.Update + .SetOnInsert(doc => doc.Id, studentNotifications.UserId) + .PushEach(doc => doc.Notifications, studentNotifications.Notifications.Select(n => n.AsDocument())); + + var options = new UpdateOptions { IsUpsert = true }; + await _repository.Collection.UpdateOneAsync(filter, update, options); + } + + public async Task AddOrUpdateAsync(UserNotifications studentNotifications) + { + var document = studentNotifications.AsDocument(); + var filter = Builders.Filter.Eq(doc => doc.UserId, studentNotifications.UserId); + + var initializationUpdate = Builders.Update + .SetOnInsert(doc => doc.Notifications, new List()) + .SetOnInsert(doc => doc.Id, document.Id); + + var addToSetUpdates = new List>(); + foreach (var notification in studentNotifications.Notifications) + { + var notificationDocument = notification.AsDocument(); + var notificationFilter = Builders.Filter.And( + Builders.Filter.Eq("Notifications.Message", notificationDocument.Message), + Builders.Filter.Eq("Notifications.CreatedAt", notificationDocument.CreatedAt), + Builders.Filter.Eq("Notifications.EventType", notificationDocument.EventType) + ); + + var combinedFilter = Builders.Filter.And(filter, notificationFilter); + var update = Builders.Update.AddToSet(doc => doc.Notifications, notificationDocument); + + addToSetUpdates.Add(update); + } + + var options = new UpdateOptions { IsUpsert = true }; + + await _repository.Collection.UpdateOneAsync(filter, initializationUpdate, options); + + foreach (var update in addToSetUpdates) + { + await _repository.Collection.UpdateOneAsync(filter, update, options); + } + } + + public async Task> FindAsync(FilterDefinition filter, FindOptions options) + { + var documents = await _repository.Collection.Find(filter, options).ToListAsync(); + return documents.Select(doc => doc.AsEntity()).ToList(); + } + + + public Task DeleteAsync(Guid studentId) + { + return _repository.DeleteAsync(studentId); + } + + public async Task BulkUpdateAsync(FilterDefinition filter, UpdateDefinition update) + { + return await _repository.Collection.UpdateManyAsync(filter, update); + } + + public async Task UpdateNotificationStatus(Guid studentId, Guid notificationId, string newStatus) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(doc => doc.UserId, studentId), + Builders.Filter.ElemMatch(doc => doc.Notifications, n => n.NotificationId == notificationId) + ); + + var update = Builders.Update + .Set("Notifications.$.Status", newStatus) + .CurrentDate("Notifications.$.UpdatedAt"); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task NotificationExists(Guid studentId, Guid notificationId) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(doc => doc.UserId, studentId), + Builders.Filter.ElemMatch(doc => doc.Notifications, n => n.NotificationId == notificationId) + ); + + var result = await _repository.Collection.Find(filter).AnyAsync(); + return result; + } + + public async Task DeleteNotification(Guid studentId, Guid notificationId) + { + var filter = Builders.Filter.And( + Builders.Filter.Eq(d => d.UserId, studentId), + Builders.Filter.ElemMatch(e => e.Notifications, n => n.NotificationId == notificationId)); + + var update = Builders.Update.PullFilter( + p => p.Notifications, n => n.NotificationId == notificationId); + + await _repository.Collection.UpdateOneAsync(filter, update); + } + + public async Task GetNotificationCount(Guid studentId) + { + var studentNotifications = await _repository.GetAsync(x => x.UserId == studentId); + return studentNotifications?.Notifications.Count ?? 0; + } + + public async Task> GetRecentNotifications(Guid studentId, int count) + { + var filter = Builders.Filter.Eq(d => d.UserId, studentId); + + var options = new FindOptions + { + Sort = Builders.Sort.Descending(d => d.Notifications[-1].CreatedAt), + Projection = Builders.Projection.Slice(p => p.Notifications, -count) + }; + + var cursor = await _repository.Collection.FindAsync(filter, options); + + var documents = await cursor.ToListAsync(); + var notifications = new List(); + foreach (var document in documents) + { + if (document.Notifications != null) + { + notifications.AddRange(document.Notifications); + } + } + + return notifications; + } + } +} diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/BaseUrlService.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/BaseUrlService.cs new file mode 100644 index 000000000..5f7fdb91a --- /dev/null +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/BaseUrlService.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using MiniSpace.Services.Notifications.Application.Services; + +namespace MiniSpace.Services.Notifications.Infrastructure.Services +{ + public class BaseUrlService : IBaseUrlService + { + private readonly IConfiguration _configuration; + + public BaseUrlService(IConfiguration configuration) + { + _configuration = configuration; + } + + public string GetBaseUrl() + { + var environment = _configuration["ASPNETCORE_ENVIRONMENT"]; + return environment == "Production" + ? "https://astraven.com" + : "http://localhost:5606"; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/CommentsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/CommentsServiceClient.cs index 420c7a29c..8a38a1539 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/CommentsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/CommentsServiceClient.cs @@ -2,8 +2,8 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Convey.HTTP; -using MiniSpace.Services.Notifications.Application.Dto; +using Paralax.HTTP; +using MiniSpace.Services.Notifications.Application.Dto.Comments; using MiniSpace.Services.Notifications.Application.Services.Clients; namespace MiniSpace.Services.Notifications.Infrastructure.Services.Clients diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/EventsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/EventsServiceClient.cs index a82033dbe..1a130b343 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/EventsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/EventsServiceClient.cs @@ -3,8 +3,9 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Dto.Events; using MiniSpace.Services.Notifications.Application.Services.Clients; namespace MiniSpace.Services.Notifications.Infrastructure.Services.Clients @@ -43,7 +44,6 @@ private async Task HandleResponseAsync(HttpResponseMessage response) where } var json = await response.Content.ReadAsStringAsync(); - // Console.WriteLine("JSON Response: " + json); try { @@ -54,12 +54,10 @@ private async Task HandleResponseAsync(HttpResponseMessage response) where if (responseObject == null) { - // Console.WriteLine("Deserialized object is null. Possibly empty JSON."); return null; } var jsonString = JsonSerializer.Serialize(responseObject, new JsonSerializerOptions { WriteIndented = true }); - // Console.WriteLine("Deserialized JSON Object: " + jsonString); return responseObject; } diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/FriendsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/FriendsServiceClient.cs index 347ea8a75..05dc7356e 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/FriendsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/FriendsServiceClient.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Services.Clients; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/PostsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/PostsServiceClient.cs index e166db6c9..41670153f 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/PostsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/PostsServiceClient.cs @@ -3,8 +3,9 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Notifications.Application.Dto; +using MiniSpace.Services.Notifications.Application.Dto.Posts; using MiniSpace.Services.Notifications.Application.Services.Clients; namespace MiniSpace.Services.Notifications.Infrastructure.Services.Clients diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReactionsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReactionsServiceClient.cs index 396aac424..a291d4d1a 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReactionsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReactionsServiceClient.cs @@ -3,7 +3,7 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Services.Clients; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReportsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReportsServiceClient.cs index d7604d7bf..0029c9ef9 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReportsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/ReportsServiceClient.cs @@ -2,7 +2,7 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Services.Clients; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/StudentsServiceClient.cs index 9dcd94906..767391952 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -1,7 +1,7 @@ using System; using System.Text.Json; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Notifications.Application.Dto; using MiniSpace.Services.Notifications.Application.Queries; using MiniSpace.Services.Notifications.Application.Services.Clients; @@ -19,10 +19,10 @@ public StudentsServiceClient(IHttpClient httpClient, HttpClientOptions options) _url = options.Services["students"]; } - public Task GetAsync(Guid id) - => _httpClient.GetAsync($"{_url}/students/{id}"); + public Task GetAsync(Guid id) + => _httpClient.GetAsync($"{_url}/students/{id}"); - public async Task> GetAllAsync() + public async Task> GetAllAsync() { var response = await _httpClient.GetAsync($"{_url}/students"); var json = await response.Content.ReadAsStringAsync(); @@ -36,7 +36,7 @@ public async Task> GetAllAsync() var jsonDocument = JsonDocument.Parse(json); if (jsonDocument.RootElement.TryGetProperty("results", out JsonElement resultsElement)) { - var students = JsonSerializer.Deserialize>(resultsElement.GetRawText(), new JsonSerializerOptions + var students = JsonSerializer.Deserialize>(resultsElement.GetRawText(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/DailyNotificationCleanupService.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/DailyNotificationCleanupService.cs index 4815c97a1..9d720fbeb 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/DailyNotificationCleanupService.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/DailyNotificationCleanupService.cs @@ -12,11 +12,11 @@ public class DailyNotificationCleanupService : BackgroundService { private readonly ILogger _logger; - private readonly IExtendedStudentNotificationsRepository _notificationsRepository; + private readonly IExtendedUserNotificationsRepository _notificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; public DailyNotificationCleanupService(ILogger logger, - IExtendedStudentNotificationsRepository notificationsRepository, + IExtendedUserNotificationsRepository notificationsRepository, IStudentsServiceClient studentsServiceClient) { _logger = logger; @@ -42,12 +42,12 @@ private async Task RemoveReadNotifications(CancellationToken stoppingToken) foreach (var student in allStudents) { var studentId = student.Id; - var filter = Builders.Filter.And( - Builders.Filter.Eq(doc => doc.StudentId, studentId), - Builders.Filter.ElemMatch(n => n.Notifications, n => n.Status == "Read") + var filter = Builders.Filter.And( + Builders.Filter.Eq(doc => doc.UserId, studentId), + Builders.Filter.ElemMatch(n => n.Notifications, n => n.Status == "Read") ); - var update = Builders.Update.PullFilter( + var update = Builders.Update.PullFilter( n => n.Notifications, n => n.Status == "Read"); var result = await _notificationsRepository.BulkUpdateAsync(filter, update); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/EventMapper.cs index fc82f6fcc..f315fb451 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Notifications.Application.Services; using MiniSpace.Services.Notifications.Core; using MiniSpace.Services.Notifications.Core.Entities; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/MessageBroker.cs index 647d566a9..0e42ac54f 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/NotificationCleanupService.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/NotificationCleanupService.cs index 7e8356a12..6075fdce9 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/NotificationCleanupService.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/NotificationCleanupService.cs @@ -12,11 +12,11 @@ public class NotificationCleanupService : BackgroundService { private readonly ILogger _logger; - private readonly IExtendedStudentNotificationsRepository _notificationsRepository; + private readonly IExtendedUserNotificationsRepository _notificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; public NotificationCleanupService(ILogger logger, - IExtendedStudentNotificationsRepository notificationsRepository, + IExtendedUserNotificationsRepository notificationsRepository, IStudentsServiceClient studentsServiceClient) { _logger = logger; @@ -63,8 +63,8 @@ private async Task CleanupOldNotifications() { _logger.LogInformation($"Removing all notifications for student {studentId} due to excessive count: {count}."); - var filter = Builders.Filter.Eq(doc => doc.StudentId, studentId); - var update = Builders.Update.Set(doc => doc.Notifications, new List()); + var filter = Builders.Filter.Eq(doc => doc.UserId, studentId); + var update = Builders.Update.Set(doc => doc.Notifications, new List()); var result = await _notificationsRepository.BulkUpdateAsync(filter, update); _logger.LogInformation($"All notifications for student {studentId} have been removed. Total removed: {result.ModifiedCount}"); @@ -73,12 +73,12 @@ private async Task CleanupOldNotifications() { _logger.LogInformation($"Cleaning up old notifications for student {studentId}. Total current count: {count}."); - var filter = Builders.Filter.And( - Builders.Filter.Eq(doc => doc.StudentId, studentId), - Builders.Filter.Lt("Notifications.CreatedAt", cutoffDate) + var filter = Builders.Filter.And( + Builders.Filter.Eq(doc => doc.UserId, studentId), + Builders.Filter.Lt("Notifications.CreatedAt", cutoffDate) ); - var update = Builders.Update.PullFilter( + var update = Builders.Update.PullFilter( n => n.Notifications, n => n.CreatedAt < cutoffDate); var result = await _notificationsRepository.BulkUpdateAsync(filter, update); @@ -102,12 +102,12 @@ private async Task RemoveReadNotifications() foreach (var student in allStudents) { var studentId = student.Id; - var filter = Builders.Filter.And( - Builders.Filter.Eq(doc => doc.StudentId, studentId), - Builders.Filter.ElemMatch(n => n.Notifications, n => n.Status == "Read") + var filter = Builders.Filter.And( + Builders.Filter.Eq(doc => doc.UserId, studentId), + Builders.Filter.ElemMatch(n => n.Notifications, n => n.Status == "Read") ); - var update = Builders.Update.PullFilter( + var update = Builders.Update.PullFilter( n => n.Notifications, n => n.Status == "Read"); var result = await _notificationsRepository.BulkUpdateAsync(filter, update); diff --git a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/PeriodicNotificationCleanupService.cs b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/PeriodicNotificationCleanupService.cs index 861988810..fdc4163f0 100644 --- a/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/PeriodicNotificationCleanupService.cs +++ b/MiniSpace.Services.Notifications/src/MiniSpace.Services.Notifications.Infrastructure/Services/PeriodicNotificationCleanupService.cs @@ -12,11 +12,11 @@ public class PeriodicNotificationCleanupService : BackgroundService { private readonly ILogger _logger; - private readonly IExtendedStudentNotificationsRepository _notificationsRepository; + private readonly IExtendedUserNotificationsRepository _notificationsRepository; private readonly IStudentsServiceClient _studentsServiceClient; public PeriodicNotificationCleanupService(ILogger logger, - IExtendedStudentNotificationsRepository notificationsRepository, + IExtendedUserNotificationsRepository notificationsRepository, IStudentsServiceClient studentsServiceClient) { _logger = logger; @@ -54,8 +54,8 @@ private async Task CleanupOldNotifications(CancellationToken stoppingToken) _logger.LogInformation($"Removing all notifications for student {studentId} due to excessive count: {count}."); - var filter = Builders.Filter.Eq(doc => doc.StudentId, studentId); - var update = Builders.Update.Set(doc => doc.Notifications, new List()); + var filter = Builders.Filter.Eq(doc => doc.UserId, studentId); + var update = Builders.Update.Set(doc => doc.Notifications, new List()); var result = await _notificationsRepository.BulkUpdateAsync(filter, update); _logger.LogInformation($"All notifications for student {studentId} have been removed. Total removed: {result.ModifiedCount}"); @@ -64,12 +64,12 @@ private async Task CleanupOldNotifications(CancellationToken stoppingToken) { _logger.LogInformation($"Cleaning up old notifications for student {studentId}. Total current count: {count}."); - var filter = Builders.Filter.And( - Builders.Filter.Eq(doc => doc.StudentId, studentId), - Builders.Filter.Lt("Notifications.CreatedAt", cutoffDate) + var filter = Builders.Filter.And( + Builders.Filter.Eq(doc => doc.UserId, studentId), + Builders.Filter.Lt("Notifications.CreatedAt", cutoffDate) ); - var update = Builders.Update.PullFilter( + var update = Builders.Update.PullFilter( n => n.Notifications, n => n.CreatedAt < cutoffDate); var result = await _notificationsRepository.BulkUpdateAsync(filter, update); diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/MiniSpace.Services.Organizations.Api.csproj b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/MiniSpace.Services.Organizations.Api.csproj index 53eb9ef71..1ba462faf 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/MiniSpace.Services.Organizations.Api.csproj +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/MiniSpace.Services.Organizations.Api.csproj @@ -1,17 +1,20 @@ + net8.0 disable enable - true + + + - - - - + + + + diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs index 2020f659e..64a02045b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Api/Program.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -13,23 +13,59 @@ using MiniSpace.Services.Organizations.Application.Commands; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; -using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Infrastructure; +using Paralax.Core; namespace MiniSpace.Services.Organizations.Api { public class Program { public static async Task Main(string[] args) - => await WebHost.CreateDefaultBuilder(args) + { + + await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() .Build()) .Configure(app => app .UseInfrastructure() + .UseEndpoints(endpoints => endpoints + .Post("organizations", async (cmd, ctx) => + { + await ctx.Response.Created($"organizations/{cmd.OrganizationId}"); + }) + .Post("organizations/{organizationId}/children", async (cmd, ctx) => + { + await ctx.Response.Created($"organizations/{cmd.SubOrganizationId}"); + }) + .Post("organizations/{organizationId}/roles", async (cmd, ctx) => + { + await ctx.Response.Created($"organizations/{cmd.OrganizationId}/roles/{cmd.RoleName}"); + }) + .Post("organizations/{organizationId}/follow", async (cmd, ctx) => + { + await ctx.Response.Created($"organizations/{cmd.OrganizationId}/follow"); + }) + .Post("organizations/{organizationId}/leave", async (cmd, ctx) => + { + await ctx.Response.NoContent(); + }) + .Put("organizations/{organizationId}/requests/{requestId}/accept", async (cmd, ctx) => + { + await ctx.Response.NoContent(); + }) + .Put("organizations/{organizationId}/requests/{requestId}/reject", async (cmd, ctx) => + { + await ctx.Response.NoContent(); + }) + .Delete("organizations/{organizationId}", async (cmd, ctx) => + { + await ctx.Response.NoContent(); + }) + ) .UseDispatcherEndpoints(endpoints => endpoints .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) .Get("organizations/{organizationId}") @@ -37,42 +73,16 @@ public static async Task Main(string[] args) .Get>("organizations/root") .Get>("organizations/{organizationId}/children") .Get>("organizations/{organizationId}/children/all") - // the organizations users is the organizer .Get>("organizations/users/{userId}/organizations") - // organizations, user is a part of .Get>("organizations/users/{userId}/organizations/follow") .Get("organizations/{organizationId}/details/gallery-users") .Get>("organizations/{organizationId}/roles") .Get>("organizations/paginated") .Get>("organizations/{organizationId}/requests") - - - .Post("organizations", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"organizations/{cmd.OrganizationId}")) - .Post("organizations/{organizationId}/children", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"organizations/{cmd.SubOrganizationId}")) - .Post("organizations/{organizationId}/roles", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"organizations/{cmd.OrganizationId}/roles/{cmd.RoleName}")) - .Post("organizations/{organizationId}/follow", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"organizations/{cmd.OrganizationId}/follow")) - .Put("organizations/{organizationId}/requests/{requestId}/accept", - afterDispatch: (cmd, ctx) => ctx.Response.NoContent()) - .Post("organizations/{organizationId}/leave", - afterDispatch: (cmd, ctx) => ctx.Response.NoContent()) - .Put("organizations/{organizationId}/requests/{requestId}/reject") - .Delete("organizations/{organizationId}") - .Post("organizations/{organizationId}/invite") - .Post("organizations/{organizationId}/roles/{memberId}") - .Put("organizations/{organizationId}/roles/{roleId}/permissions") - .Post("organizations/{organizationId}/privacy") - .Put("organizations/{organizationId}/settings") - .Put("organizations/{organizationId}/visibility") - .Put("organizations/{organizationId}/feed") - .Put("organizations/{organizationId}", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"organizations/{cmd.OrganizationId}")) - )) + )) .UseLogging() .Build() .RunAsync(); + } } } diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AcceptFollowRequest.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AcceptFollowRequest.cs index 1f60001e0..539d9f9f9 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AcceptFollowRequest.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AcceptFollowRequest.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AssignRoleToMember.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AssignRoleToMember.cs index 17d03c1c5..c577f9742 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AssignRoleToMember.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/AssignRoleToMember.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Organizations.Application.Commands { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganization.cs index 1f67dd99b..8bf8239f5 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Core.Entities; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganizationRole.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganizationRole.cs index 6aa925b7c..3eefeb31b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganizationRole.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateOrganizationRole.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateSubOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateSubOrganization.cs index cbe1555d2..5fb2efd06 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateSubOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/CreateSubOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Core.Entities; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/DeleteOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/DeleteOrganization.cs index d97bc948e..3e73c86e3 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/DeleteOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/DeleteOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Organizations.Application.Commands { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/FollowOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/FollowOrganization.cs index 21f036566..bc3ccdef7 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/FollowOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/FollowOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AcceptFollowRequestHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AcceptFollowRequestHandler.cs index 1e78cafc4..efab4e017 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AcceptFollowRequestHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AcceptFollowRequestHandler.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Core.Entities; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AssignRoleToMemberHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AssignRoleToMemberHandler.cs index d9c12f49e..8e1ae16e3 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AssignRoleToMemberHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/AssignRoleToMemberHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Services; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs index a5614c320..ea5260755 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Services; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationRoleHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationRoleHandler.cs index a3c73fdfb..ae33628fc 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationRoleHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateOrganizationRoleHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateSubOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateSubOrganizationHandler.cs index f89248c77..ba5c280bc 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateSubOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/CreateSubOrganizationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Services; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/DeleteOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/DeleteOrganizationHandler.cs index be04e33fd..c59c9c6f6 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/DeleteOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/DeleteOrganizationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Services; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/FollowOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/FollowOrganizationHandler.cs index 5eeed3c65..6bcb8532d 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/FollowOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/FollowOrganizationHandler.cs @@ -1,8 +1,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Core.Entities; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/InviteUserToOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/InviteUserToOrganizationHandler.cs index 803666a3b..1dcc963bf 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/InviteUserToOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/InviteUserToOrganizationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Services; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/LeaveOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/LeaveOrganizationHandler.cs index 69dcf9b38..391273ac7 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/LeaveOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/LeaveOrganizationHandler.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Core.Entities; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RejectFollowRequestHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RejectFollowRequestHandler.cs index 0d4777a40..e5767f891 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RejectFollowRequestHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/RejectFollowRequestHandler.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationHandler.cs index 83d02805d..379ad7ccb 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Services; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationSettingsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationSettingsHandler.cs index 33414214f..3968a51a8 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationSettingsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateOrganizationSettingsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Application.Events; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Application.Services; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateRolePermissionsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateRolePermissionsHandler.cs index e653b2943..3f37a1a41 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateRolePermissionsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/Handlers/UpdateRolePermissionsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Core.Entities; using System; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/InviteUserToOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/InviteUserToOrganization.cs index 4cf66956f..8bfd51f9c 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/InviteUserToOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/InviteUserToOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Organizations.Application.Commands { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/LeaveOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/LeaveOrganization.cs index 805a7687b..224b8505b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/LeaveOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/LeaveOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/ManageFeed.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/ManageFeed.cs index 59374c613..51617d9b6 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/ManageFeed.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/ManageFeed.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Organizations.Application.Commands { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RejectFollowRequest.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RejectFollowRequest.cs index b9530ea90..a92bc1324 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RejectFollowRequest.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/RejectFollowRequest.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationPrivacy.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationPrivacy.cs index 569342799..efa3a959a 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationPrivacy.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationPrivacy.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Organizations.Application.Commands { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationVisibility.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationVisibility.cs index 008e6816c..4715bbe79 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationVisibility.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/SetOrganizationVisibility.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Organizations.Application.Commands { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganization.cs index 0bc566021..1984749c1 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Core.Entities; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganizationSettings.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganizationSettings.cs index 4578bdbdf..dfabc4b17 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganizationSettings.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateOrganizationSettings.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Organizations.Core.Entities; namespace MiniSpace.Services.Organizations.Application.Commands diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateRolePermissions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateRolePermissions.cs index 235817895..fa1945815 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateRolePermissions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Commands/UpdateRolePermissions.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Organizations.Application.Commands { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/MediaFileDeletedHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/MediaFileDeletedHandler.cs index 51a89283b..63ee612ba 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/MediaFileDeletedHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/MediaFileDeletedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Core.Repositories; using System.Text.Json; using System.Threading; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/OrganizationImageUploadedHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/OrganizationImageUploadedHandler.cs index b95c63ef2..53386b42a 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/OrganizationImageUploadedHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/Handlers/OrganizationImageUploadedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Core.Entities; using System.Threading; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/MediaFileDeleted.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/MediaFileDeleted.cs index b8215559c..cb3b1b56b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/MediaFileDeleted.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/MediaFileDeleted.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Organizations.Application.Events.External diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/OrganizationImageUploaded.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/OrganizationImageUploaded.cs index e9cb95ce9..8f31acbda 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/OrganizationImageUploaded.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/External/OrganizationImageUploaded.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Organizations.Application.Events.External diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationCreated.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationCreated.cs index a63495c5e..e56b677d9 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationCreated.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Organizations.Application.Events diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationDeleted.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationDeleted.cs index 7ec21e87e..c03a19f69 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationDeleted.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Organizations.Application.Events { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationSettingsUpdated.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationSettingsUpdated.cs index 8f053cb11..f58c4e763 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationSettingsUpdated.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationSettingsUpdated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Core.Entities; using System; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationUpserted.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationUpserted.cs index b4c67acd1..c9cd18669 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationUpserted.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizationUpserted.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Organizations.Application.Events { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizerAddedToOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizerAddedToOrganization.cs index 82e1470a6..35e1b313d 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizerAddedToOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/OrganizerAddedToOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Organizations.Application.Events { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RoleAssignedToMember.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RoleAssignedToMember.cs index d27fe3d4e..4e12149dd 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RoleAssignedToMember.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RoleAssignedToMember.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Organizations.Application.Events diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RootOrganizationCreated.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RootOrganizationCreated.cs index 2a67ec497..95e6499ff 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RootOrganizationCreated.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/RootOrganizationCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Organizations.Application.Events diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserAddedToOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserAddedToOrganization.cs index 23090f004..69ba5fc15 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserAddedToOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserAddedToOrganization.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Organizations.Application.Events { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserInvitedToOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserInvitedToOrganization.cs index 06be75b28..49934107c 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserInvitedToOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserInvitedToOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Organizations.Application.Events diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserRemovedFromOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserRemovedFromOrganization.cs index ee00eef6a..2eb68035d 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserRemovedFromOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Events/UserRemovedFromOrganization.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Organizations.Application.Events { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Extensions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Extensions.cs index 250b71ee3..abf7ba0c0 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Extensions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Extensions.cs @@ -1,6 +1,6 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Organizations.Application @@ -8,7 +8,7 @@ namespace MiniSpace.Services.Organizations.Application [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/MiniSpace.Services.Organizations.Application.csproj b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/MiniSpace.Services.Organizations.Application.csproj index 17363c346..2e794c1a7 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/MiniSpace.Services.Organizations.Application.csproj +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/MiniSpace.Services.Organizations.Application.csproj @@ -11,11 +11,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetAllChildrenOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetAllChildrenOrganizations.cs index dc365f69d..628aac857 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetAllChildrenOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetAllChildrenOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs index 341502700..50c05299b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetChildrenOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs index 147f1c429..dbf448bba 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganization.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs index 52edd870a..7eb074dab 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationDetails.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationRoles.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationRoles.cs index 2b6e76059..d18f58dec 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationRoles.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationRoles.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGallery.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGallery.cs index 717040216..bbef73d26 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGallery.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGallery.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGalleryAndUsers.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGalleryAndUsers.cs index b77056d08..0abb27b1e 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGalleryAndUsers.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithGalleryAndUsers.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithUsers.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithUsers.cs index a8f2b3094..59804d6ea 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithUsers.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetOrganizationWithUsers.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizationRequests.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizationRequests.cs index 053e4eee9..24fe04f6e 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizationRequests.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizationRequests.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; namespace MiniSpace.Services.Organizations.Application.Queries diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizations.cs index 3afc03085..dbf9e6f82 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedUserOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedUserOrganizations.cs index 75bbb18ce..b0e218713 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedUserOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetPaginatedUserOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetRootOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetRootOrganizations.cs index 9b49d0835..c8b80300c 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetRootOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetRootOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserCreatedOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserCreatedOrganizations.cs index a9cba871f..26d599282 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserCreatedOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserCreatedOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserFollowOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserFollowOrganizations.cs index 4491d8ee6..f4a62f11b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserFollowOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserFollowOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserOrganizations.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserOrganizations.cs index d03b379b7..ac5d9a2f6 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserOrganizations.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Queries/GetUserOrganizations.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IEventMapper.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IEventMapper.cs index 8f67b1913..05defea64 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Core.Events; namespace MiniSpace.Services.Organizations.Application.Services diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IMessageBroker.cs index 0270ca9fd..7189c4564 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Organizations.Application.Services { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Contexts/AppContextFactory.cs index 4d96dc46a..c95e005fb 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Organizations.Application; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 247c6452d..f9c45e031 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Organizations.Infrastructure.Decorators diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index cbe3777d8..d45dbfa54 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Organizations.Infrastructure.Decorators diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 62ec3951b..7f978f8eb 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; // using MiniSpace.Services.Organizations.Application.Commands; // using MiniSpace.Services.Organizations.Application.Events.Rejected; // using MiniSpace.Services.Organizations.Application.Events.External; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index e6e53792d..f70709420 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Organizations.Application.Exceptions; using MiniSpace.Services.Organizations.Core.Exceptions; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs index 43dc89b40..92ce5e22c 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -45,7 +45,7 @@ namespace MiniSpace.Services.Organizations.Infrastructure [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddScoped(); @@ -98,7 +98,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs index 5feb4a9ab..fe5612b3b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/Extensions.cs @@ -1,5 +1,5 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; +using Paralax.CQRS.Logging; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Organizations.Application.Commands; using System.Diagnostics.CodeAnalysis; @@ -9,7 +9,7 @@ namespace MiniSpace.Services.Organizations.Infrastructure.Logging [ExcludeFromCodeCoverage] internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(CreateOrganization).Assembly; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 5471c9a02..4f07cd464 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,4 +1,4 @@ -using Convey.Logging.CQRS; +using Paralax.CQRS.Logging; using MiniSpace.Services.Organizations.Application.Commands; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/MiniSpace.Services.Organizations.Infrastructure.csproj b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/MiniSpace.Services.Organizations.Infrastructure.csproj index 777216a0c..efd7db04f 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/MiniSpace.Services.Organizations.Infrastructure.csproj +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/MiniSpace.Services.Organizations.Infrastructure.csproj @@ -1,10 +1,5 @@  - - - - - net8.0 enable @@ -12,29 +7,29 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -42,4 +37,16 @@ + + + + + + + + + + + + diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs index 4b47ea7b5..27ec7b26b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Organizations.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationGalleryImageDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationGalleryImageDocument.cs index 98271f7c6..367f1dbb3 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationGalleryImageDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationGalleryImageDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationInvitationDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationInvitationDocument.cs index ecd45b692..4632eb582 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationInvitationDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationInvitationDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationMembersDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationMembersDocument.cs index 7e5bb38be..5a94714d8 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationMembersDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationMembersDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRequestsDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRequestsDocument.cs index a2a71dae8..97d8a907a 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRequestsDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRequestsDocument.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRolesDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRolesDocument.cs index 0e644f8a3..393f3e706 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRolesDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/OrganizationRolesDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Organizations.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/UserOrganizationsDocument.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/UserOrganizationsDocument.cs index 0e69547ad..93cf0169a 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/UserOrganizationsDocument.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Documents/UserOrganizationsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetAllChildrenOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetAllChildrenOrganizationsHandler.cs index 9862838ba..7a1d4b6e5 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetAllChildrenOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetAllChildrenOrganizationsHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs index 2129ef31c..9904ca6c3 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetChildrenOrganizationsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Entities; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs index b2d695cda..37090d345 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationDetailsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs index 0c649de62..eda08e25d 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationRolesHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationRolesHandler.cs index 8474b4781..7693242cc 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationRolesHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationRolesHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs index 89438b13f..b1b2013f3 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryAndUsersHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryHandler.cs index 6b5a9e162..c6380b08b 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithGalleryHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithUsersHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithUsersHandler.cs index 2de49aee4..5c3d01c75 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithUsersHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetOrganizationWithUsersHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationRequestsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationRequestsHandler.cs index 92308c268..17d36c416 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationRequestsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationRequestsHandler.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationsHandler.cs index f31a57baf..ef78472f4 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedOrganizationsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserOrganizationsHandler.cs index 616b84d93..fbb72978c 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetPaginatedUserOrganizationsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs index 7d38ab7c1..cea675c81 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetRootOrganizationsHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserFollowOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserFollowOrganizationsHandler.cs index 0b05cb375..941c8e7e9 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserFollowOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserFollowOrganizationsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs index 9e82705d9..d913c4f3a 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Queries/Handlers/GetUserOrganizationsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Organizations.Application.DTO; using MiniSpace.Services.Organizations.Application.Queries; using MiniSpace.Services.Organizations.Core.Repositories; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationGalleryMongoRepository.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationGalleryMongoRepository.cs index 836728e8b..2a8ceb945 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationGalleryMongoRepository.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationGalleryMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMembersMongoRepository.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMembersMongoRepository.cs index db8a2e443..c56a7c2f3 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMembersMongoRepository.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMembersMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMongoRepository.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMongoRepository.cs index bae6eb540..2bced7d1d 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMongoRepository.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRequestsMongoRepository.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRequestsMongoRepository.cs index 75b5d9b55..e62e47b06 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRequestsMongoRepository.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRequestsMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRolesMongoRepository.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRolesMongoRepository.cs index b653c25ca..0c7ee4ea0 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRolesMongoRepository.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/OrganizationRolesMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserInvitationsMongoRepository.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserInvitationsMongoRepository.cs index ecca33727..4f8bb3fa8 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserInvitationsMongoRepository.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserInvitationsMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserOrganizationsMongoRepository.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserOrganizationsMongoRepository.cs index 88a02987b..1bc6cd0dd 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserOrganizationsMongoRepository.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Mongo/Repositories/UserOrganizationsMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Organizations.Core.Entities; using MiniSpace.Services.Organizations.Core.Repositories; using MiniSpace.Services.Organizations.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/EventMapper.cs index 632373690..1fff15cf5 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Organizations.Application.Services; using MiniSpace.Services.Organizations.Core; using MiniSpace.Services.Organizations.Core.Events; diff --git a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/MessageBroker.cs index 0601896b1..2f7bdcce5 100644 --- a/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Organizations/src/MiniSpace.Services.Organizations.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/MiniSpace.Services.Posts.Api.csproj b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/MiniSpace.Services.Posts.Api.csproj index d04753593..e02a72d34 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/MiniSpace.Services.Posts.Api.csproj +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/MiniSpace.Services.Posts.Api.csproj @@ -7,14 +7,20 @@ - - - - + + + + + + + diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs index a7b91bf1b..32b4c607f 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Api/Program.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.Core; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -19,6 +19,8 @@ using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Core.Wrappers; using MiniSpace.Services.Posts.Infrastructure; +using Paralax.Types; + namespace MiniSpace.Services.Posts.Api { @@ -28,7 +30,7 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() @@ -51,6 +53,8 @@ public static async Task Main(string[] args) afterDispatch: (cmd, ctx) => ctx.Response.Created($"posts/{cmd.PostId}")) .Put("posts/{postId}/state/{state}", afterDispatch: (cmd, ctx) => ctx.Response.NoContent()) + .Post("posts/{originalPostId}/repost", + afterDispatch: (cmd, ctx) => ctx.Response.Created($"posts/{cmd.RepostedPostId}/reposted")) )) .UseLogging() .Build() diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs index 4f49f0910..e197ba444 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ChangePostState.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Core.Entities; using System; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs index 139d008f6..37d0e5f13 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/CreatePost.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Core.Entities; using System; using System.Collections.Generic; @@ -16,10 +16,28 @@ public class CreatePost : ICommand public string State { get; } public DateTime? PublishDate { get; } public PostContext Context { get; } - public string Visibility { get; set; } + public string Visibility { get; set; } + public string PostType { get; } + public string Title { get; } - public CreatePost(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, - IEnumerable mediaFiles, string state, DateTime? publishDate, PostContext context, string visibility) + public Guid? PageOwnerId { get; } + public string PageOwnerType { get; } + + public CreatePost( + Guid postId, + Guid? userId, + Guid? organizationId, + Guid? eventId, + string textContent, + IEnumerable mediaFiles, + string state, + DateTime? publishDate, + PostContext context, + string visibility, + string postType, + string title = null, + Guid? pageOwnerId = null, + string pageOwnerType = "User") { PostId = postId; UserId = userId; @@ -30,7 +48,11 @@ public CreatePost(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId State = state; PublishDate = publishDate; Context = context; - Visibility = visibility; + Visibility = visibility; + PostType = postType; + Title = title; + PageOwnerId = pageOwnerId; + PageOwnerType = pageOwnerType; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs index b397106f5..f6b5e255a 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/DeletePost.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Core.Entities; using System; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs index dcaebba7e..b49a6ba3a 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ChangePostStateHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Application.Events; using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Application.Services; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs index 37046bde9..4a8bfc13c 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/CreatePostHandler.cs @@ -1,12 +1,9 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Application.Events; using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Application.Services; -using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Exceptions; -using MiniSpace.Services.Posts.Core.Repositories; using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -14,108 +11,30 @@ namespace MiniSpace.Services.Posts.Application.Commands.Handlers { public class CreatePostHandler : ICommandHandler { - private readonly IPostRepository _postRepository; - private readonly IUserPostRepository _userPostRepository; - private readonly IOrganizationPostRepository _organizationPostRepository; - private readonly IUserEventPostRepository _userEventPostRepository; - private readonly IOrganizationEventPostRepository _organizationEventPostRepository; - private readonly IDateTimeProvider _dateTimeProvider; + private readonly IPostsService _postsService; private readonly IMessageBroker _messageBroker; - private readonly IAppContext _appContext; - public CreatePostHandler( - IPostRepository postRepository, - IUserPostRepository userPostRepository, - IOrganizationPostRepository organizationPostRepository, - IUserEventPostRepository userEventPostRepository, - IOrganizationEventPostRepository organizationEventPostRepository, - IDateTimeProvider dateTimeProvider, - IMessageBroker messageBroker, - IAppContext appContext) + public CreatePostHandler(IPostsService postsService, IMessageBroker messageBroker) { - _postRepository = postRepository; - _userPostRepository = userPostRepository; - _organizationPostRepository = organizationPostRepository; - _userEventPostRepository = userEventPostRepository; - _organizationEventPostRepository = organizationEventPostRepository; - _dateTimeProvider = dateTimeProvider; + _postsService = postsService; _messageBroker = messageBroker; - _appContext = appContext; } public async Task HandleAsync(CreatePost command, CancellationToken cancellationToken = default) { - var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != command.UserId && identity.Id != command.OrganizationId) - { - throw new UnauthorizedPostCreationAttemptException(identity.Id, command.EventId ?? Guid.Empty); - } - - if (command.PostId == Guid.Empty || await _postRepository.ExistsAsync(command.PostId)) - { - throw new InvalidPostIdException(command.PostId); - } - - if (!Enum.TryParse(command.State, true, out var newState)) - { - throw new InvalidPostStateException(command.State); - } - - if (!Enum.TryParse(command.Visibility, true, out var visibilityStatus)) - { - throw new InvalidVisibilityStatusException(command.Visibility); - } - - var mediaFiles = command.MediaFiles.ToList(); - if (mediaFiles.Count > 12) - { - throw new InvalidNumberOfPostMediaFilesException(command.PostId, mediaFiles.Count); - } - - switch (newState) - { - case State.Reported: - throw new NotAllowedPostStateException(command.PostId, newState); - case State.ToBePublished when command.PublishDate is null: - throw new PublishDateNullException(command.PostId, newState); - } - - Post post; - - if (command.Context == PostContext.UserPage) - { - post = Post.CreateForUser(command.PostId, command.UserId.Value, command.TextContent, command.MediaFiles, - _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); - await _userPostRepository.AddAsync(post); - } - else if (command.Context == PostContext.OrganizationPage) - { - post = Post.CreateForOrganization(command.PostId, command.OrganizationId.Value, command.UserId, command.TextContent, command.MediaFiles, - _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); - await _organizationPostRepository.AddAsync(post); - } - else if (command.Context == PostContext.EventPage) - { - if (command.UserId.HasValue) - { - post = Post.CreateForEvent(command.PostId, command.EventId.Value, command.UserId, command.OrganizationId, command.TextContent, - command.MediaFiles, _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); - await _userEventPostRepository.AddAsync(post); - } - else - { - post = Post.CreateForEvent(command.PostId, command.EventId.Value, null, command.OrganizationId, command.TextContent, - command.MediaFiles, _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); - await _organizationEventPostRepository.AddAsync(post); - } - } - else - { - throw new InvalidPostContextException(command.Context.ToString()); - } - - await _messageBroker.PublishAsync(new PostCreated(command.PostId, post.MediaFiles)); + var post = await _postsService.CreatePostAsync(command); + + await _messageBroker.PublishAsync(new PostCreated( + command.PostId, + command.UserId, + command.OrganizationId, + command.EventId, + command.TextContent, + command.MediaFiles, + command.Context.ToString(), + command.Visibility, + shouldNotify: true + )); } - } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs index 811a2923f..fd5619d4e 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/DeletePostHandler.cs @@ -1,11 +1,6 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Application.Events; -using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Application.Services; -using MiniSpace.Services.Posts.Core.Entities; -using MiniSpace.Services.Posts.Core.Repositories; -using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -13,88 +8,19 @@ namespace MiniSpace.Services.Posts.Application.Commands.Handlers { public class DeletePostHandler : ICommandHandler { - private readonly IUserPostRepository _userPostRepository; - private readonly IOrganizationPostRepository _organizationPostRepository; - private readonly IUserEventPostRepository _userEventPostRepository; - private readonly IOrganizationEventPostRepository _organizationEventPostRepository; - private readonly IAppContext _appContext; + private readonly IPostsService _postsService; private readonly IMessageBroker _messageBroker; - public DeletePostHandler( - IUserPostRepository userPostRepository, - IOrganizationPostRepository organizationPostRepository, - IUserEventPostRepository userEventPostRepository, - IOrganizationEventPostRepository organizationEventPostRepository, - IAppContext appContext, - IMessageBroker messageBroker) + public DeletePostHandler(IPostsService postsService, IMessageBroker messageBroker) { - _userPostRepository = userPostRepository; - _organizationPostRepository = organizationPostRepository; - _userEventPostRepository = userEventPostRepository; - _organizationEventPostRepository = organizationEventPostRepository; - _appContext = appContext; + _postsService = postsService; _messageBroker = messageBroker; } public async Task HandleAsync(DeletePost command, CancellationToken cancellationToken = default) { - Post post = null; + await _postsService.DeletePostAsync(command); - switch (command.Context.ToLowerInvariant()) - { - case "userpage": - post = await _userPostRepository.GetAsync(command.PostId); - break; - case "organizationpage": - post = await _organizationPostRepository.GetAsync(command.PostId); - break; - case "eventpage" when command.UserId.HasValue: - post = (await _userEventPostRepository.GetByUserEventIdAsync(command.UserId.Value, command.EventId.Value)) - .FirstOrDefault(p => p.Id == command.PostId); - break; - case "eventpage" when command.OrganizationId.HasValue: - post = (await _organizationEventPostRepository.GetByOrganizationEventIdAsync(command.OrganizationId.Value, command.EventId.Value)) - .FirstOrDefault(p => p.Id == command.PostId); - break; - default: - throw new InvalidPostContextException(command.Context); - } - - if (post == null) - { - throw new PostNotFoundException(command.PostId); - } - - var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != (post.UserId ?? post.OrganizationId) && !identity.IsAdmin) - { - throw new UnauthorizedPostAccessException(command.PostId, identity.Id); - } - - if (!identity.IsAdmin && post.State == State.Reported) - { - throw new UnauthorizedPostOperationException(command.PostId, identity.Id); - } - - switch (command.Context.ToLowerInvariant()) - { - case "userpage": - await _userPostRepository.DeleteAsync(command.PostId); - break; - case "organizationpage": - await _organizationPostRepository.DeleteAsync(command.PostId); - break; - case "eventpage" when command.UserId.HasValue: - await _userEventPostRepository.DeleteAsync(command.PostId); - break; - case "eventpage" when command.OrganizationId.HasValue: - await _organizationEventPostRepository.DeleteAsync(command.PostId); - break; - default: - throw new InvalidPostContextException(command.Context); - } - - // Publish the post deleted event await _messageBroker.PublishAsync(new PostDeleted(command.PostId)); } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/RepostPostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/RepostPostHandler.cs new file mode 100644 index 000000000..67e1f9115 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/RepostPostHandler.cs @@ -0,0 +1,41 @@ +using Paralax.CQRS.Commands; +using MiniSpace.Services.Posts.Application.Services; +using MiniSpace.Services.Posts.Application.Exceptions; +using MiniSpace.Services.Posts.Core.Entities; +using System.Threading; +using System.Threading.Tasks; +using MiniSpace.Services.Posts.Application.Events; + +namespace MiniSpace.Services.Posts.Application.Commands.Handlers +{ + public class RepostPostHandler : ICommandHandler + { + private readonly IPostsService _postsService; + private readonly IMessageBroker _messageBroker; + + public RepostPostHandler(IPostsService postsService, IMessageBroker messageBroker) + { + _postsService = postsService; + _messageBroker = messageBroker; + } + + public async Task HandleAsync(RepostCommand command, CancellationToken cancellationToken = default) + { + var repostedPost = await _postsService.RepostPostAsync(command); + + var shouldNotify = true; + + await _messageBroker.PublishAsync(new PostReposted( + repostedPost.Id, + repostedPost.UserId, + repostedPost.OrganizationId, + repostedPost.EventId, + repostedPost.TextContent, + repostedPost.MediaFiles, + repostedPost.Context.ToString(), + repostedPost.Visibility.ToString(), + shouldNotify + )); + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs index fc9bd548f..8f5abd9de 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostHandler.cs @@ -1,12 +1,10 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Application.Events; using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Exceptions; -using MiniSpace.Services.Posts.Core.Repositories; using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -14,101 +12,35 @@ namespace MiniSpace.Services.Posts.Application.Commands.Handlers { public class UpdatePostHandler : ICommandHandler { - private readonly IPostRepository _postRepository; - private readonly IUserPostRepository _userPostRepository; - private readonly IOrganizationPostRepository _organizationPostRepository; - private readonly IUserEventPostRepository _userEventPostRepository; - private readonly IOrganizationEventPostRepository _organizationEventPostRepository; - private readonly IAppContext _appContext; + private readonly IPostsService _postsService; private readonly IMessageBroker _messageBroker; - private readonly IDateTimeProvider _dateTimeProvider; - public UpdatePostHandler(IPostRepository postRepository, - IUserPostRepository userPostRepository, - IOrganizationPostRepository organizationPostRepository, - IUserEventPostRepository userEventPostRepository, - IOrganizationEventPostRepository organizationEventPostRepository, - IAppContext appContext, - IMessageBroker messageBroker, - IDateTimeProvider dateTimeProvider) + public UpdatePostHandler(IPostsService postsService, IMessageBroker messageBroker) { - _postRepository = postRepository; - _userPostRepository = userPostRepository; - _organizationPostRepository = organizationPostRepository; - _userEventPostRepository = userEventPostRepository; - _organizationEventPostRepository = organizationEventPostRepository; - _appContext = appContext; + _postsService = postsService; _messageBroker = messageBroker; - _dateTimeProvider = dateTimeProvider; } public async Task HandleAsync(UpdatePost command, CancellationToken cancellationToken = default) { - // Fetch the post based on its ID - var post = await _postRepository.GetAsync(command.PostId); - if (post is null) - { - throw new PostNotFoundException(command.PostId); - } - - // Check if the identity has the right to update this post - var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != post.UserId && identity.Id != post.OrganizationId && !identity.IsAdmin) - { - throw new UnauthorizedPostAccessException(command.PostId, identity.Id); - } - - if (!identity.IsAdmin && post.State == State.Reported) - { - throw new UnauthorizedPostOperationException(command.PostId, identity.Id); - } - - // Validate the state if provided - State? newState = null; - if (!string.IsNullOrWhiteSpace(command.State)) - { - if (!Enum.TryParse(command.State, true, out var parsedState)) - { - throw new InvalidPostStateException(command.State); - } - newState = parsedState; - } - - // Validate the visibility if provided - VisibilityStatus? newVisibility = null; - if (!string.IsNullOrWhiteSpace(command.Visibility)) - { - if (!Enum.TryParse(command.Visibility, true, out var parsedVisibility)) - { - throw new InvalidVisibilityStatusException(command.Visibility); - } - newVisibility = parsedVisibility; - } - - // Validate the media files count - var mediaFiles = command.MediaFiles.ToList(); - if (mediaFiles.Count > 12) - { - throw new InvalidNumberOfPostMediaFilesException(post.Id, mediaFiles.Count); - } - - // Update the post fields - post.Update(command.TextContent, command.MediaFiles, _dateTimeProvider.Now); - - if (newState.HasValue) - { - post.ChangeState(newState.Value, command.PublishDate, _dateTimeProvider.Now); - } - - if (newVisibility.HasValue) - { - post.SetVisibility(newVisibility.Value, _dateTimeProvider.Now); - } - - await _postRepository.UpdateAsync(post); - - // Publish the post updated event - await _messageBroker.PublishAsync(new PostUpdated(post.Id, post.MediaFiles.Select(mf => new Guid(mf)))); + var post = await _postsService.UpdatePostAsync(command); + + var postContext = post.UserId.HasValue ? + "UserPage" : + post.OrganizationId.HasValue ? "OrganizationPage" : "EventPage"; + var shouldNotify = true; + + await _messageBroker.PublishAsync(new PostUpdated( + post.Id, + post.UserId, + post.OrganizationId, + post.EventId, + post.TextContent, + post.MediaFiles, + postContext, + post.Visibility.ToString(), + shouldNotify + )); } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostsStateHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostsStateHandler.cs index 03996fcb7..fddfd58c6 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostsStateHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/UpdatePostsStateHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Application.Events; using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Core.Entities; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ViewPostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ViewPostHandler.cs index 5beee8aec..91da539ff 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ViewPostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/Handlers/ViewPostHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; using Microsoft.Extensions.Logging; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/RepostCommand.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/RepostCommand.cs new file mode 100644 index 000000000..b76a57153 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/RepostCommand.cs @@ -0,0 +1,38 @@ +using Paralax.CQRS.Commands; +using MiniSpace.Services.Posts.Core.Entities; +using System; + +namespace MiniSpace.Services.Posts.Application.Commands +{ + public class RepostCommand : ICommand + { + public Guid OriginalPostId { get; } + public Guid RepostedPostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public PostContext Context { get; } + public Guid? PageOwnerId { get; } + public string PageOwnerType { get; } + + public RepostCommand( + Guid originalPostId, + Guid repostedPostId, + Guid? userId, + Guid? organizationId, + Guid? eventId, + PostContext context, + Guid? pageOwnerId = null, + string pageOwnerType = "User") + { + OriginalPostId = originalPostId; + RepostedPostId = repostedPostId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + Context = context; + PageOwnerId = pageOwnerId; + PageOwnerType = pageOwnerType; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs index ff6f50f15..f68e4a355 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePost.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Core.Entities; using System; using System.Collections.Generic; @@ -17,9 +17,27 @@ public class UpdatePost : ICommand public DateTime? PublishDate { get; } public PostContext Context { get; } public string Visibility { get; } + public string PostType { get; } + public string Title { get; } - public UpdatePost(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, - IEnumerable mediaFiles, string state, DateTime? publishDate, PostContext context, string visibility) + public Guid? PageOwnerId { get; } + public string PageOwnerType { get; } + + public UpdatePost( + Guid postId, + Guid? userId, + Guid? organizationId, + Guid? eventId, + string textContent, + IEnumerable mediaFiles, + string state, + DateTime? publishDate, + PostContext context, + string visibility, + string postType, + string title = null, + Guid? pageOwnerId = null, + string pageOwnerType = "User") { PostId = postId; UserId = userId; @@ -30,7 +48,11 @@ public UpdatePost(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId State = state; PublishDate = publishDate; Context = context; - Visibility = visibility; + Visibility = visibility; + PostType = postType; + Title = title; + PageOwnerId = pageOwnerId; + PageOwnerType = pageOwnerType; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePostsState.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePostsState.cs index a270a9ff7..f048967a1 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePostsState.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/UpdatePostsState.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Posts.Application.Commands { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ViewPost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ViewPost.cs index 494c2cbe0..636d9a266 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ViewPost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Commands/ViewPost.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Posts.Application.Commands { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs index c2dafca90..bcf1efe52 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/PostDto.cs @@ -20,6 +20,11 @@ public class PostDto public DateTime? UpdatedAt { get; set; } public string Context { get; set; } public string Visibility { get; set; } + + public Guid? PageOwnerId { get; set; } + public string PageOwnerType { get; set; } + public Guid? OriginalPostId { get; set; } + public bool IsRepost { get; set; } public PostDto() { @@ -38,7 +43,12 @@ public PostDto(Post post) CreatedAt = post.CreatedAt; UpdatedAt = post.UpdatedAt; Context = post.Context.ToString(); - Visibility = post.Visibility.ToString(); + Visibility = post.Visibility.ToString(); + + PageOwnerId = post.PageOwnerId; + PageOwnerType = post.PageOwnerType.ToString(); + OriginalPostId = post.OriginalPostId; + IsRepost = post.IsRepost; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/UserDto.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/UserDto.cs index 60ed82e35..3e087808d 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/UserDto.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Dto/UserDto.cs @@ -32,5 +32,9 @@ public class UserDto public IEnumerable SignedUpEvents { get; set; } public string Country { get; set; } public string City { get; set; } + + public bool IsOnline { get; set; } + public string DeviceType { get; set; } + public DateTime? LastActive { get; set; } } } \ No newline at end of file diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/CommentCreated.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/CommentCreated.cs index ed6d6a435..a6074eb40 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/CommentCreated.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/CommentCreated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Posts.Application.Events.External diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/EventDeleted.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/EventDeleted.cs index 1281cfba9..679baebac 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/EventDeleted.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/EventDeleted.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Posts.Application.Events.External { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/CommentCreatedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/CommentCreatedHandler.cs index 473fbece3..afaa42b0a 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/CommentCreatedHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/CommentCreatedHandler.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Posts.Application.Events.External; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs index 9fbb88231..511478b88 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/EventDeletedHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs index 9e195b729..756590b0a 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/MediaFileDeletedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Core.Repositories; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/ReactionCreatedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/ReactionCreatedHandler.cs index 38612beeb..87dda972f 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/ReactionCreatedHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/Handlers/ReactionCreatedHandler.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Posts.Application.Events.External; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs index 8f48b76ee..fdf752ba0 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/MediaFileDeleted.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Posts.Application.Events.External { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/ReactionCreated.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/ReactionCreated.cs index 87d2283e9..2309804e0 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/ReactionCreated.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/External/ReactionCreated.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Posts.Application.Events.External { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStarted.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStarted.cs index 274dd3598..9e212e51a 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStarted.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStarted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStopped.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStopped.cs index 570599956..977a08d72 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStopped.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostBackgroundWorkerStopped.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs index c707f4ef7..04d72beb7 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostCreated.cs @@ -1,16 +1,33 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; +using System; +using System.Collections.Generic; namespace MiniSpace.Services.Posts.Application.Events { public class PostCreated : IEvent { public Guid PostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public string TextContent { get; } public IEnumerable MediaFilesUrls { get; } + public string Context { get; } + public string Visibility { get; } + public bool ShouldNotify { get; } - public PostCreated(Guid postId, IEnumerable mediaFilesUrls) + public PostCreated(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, + IEnumerable mediaFilesUrls, string context, string visibility, bool shouldNotify) { PostId = postId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + TextContent = textContent; MediaFilesUrls = mediaFilesUrls; + Context = context; + Visibility = visibility; + ShouldNotify = shouldNotify; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostDeleted.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostDeleted.cs index 1b20677ad..6639bb138 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostDeleted.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostReposted.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostReposted.cs new file mode 100644 index 000000000..182045910 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostReposted.cs @@ -0,0 +1,32 @@ +using System; +using Paralax.CQRS.Events; + +namespace MiniSpace.Services.Posts.Application.Events +{ + public class PostReposted : IEvent + { + public Guid PostId { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public string TextContent { get; } + public IEnumerable MediaFiles { get; } + public string Context { get; } + public string Visibility { get; } + public bool ShouldNotify { get; } + + public PostReposted(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, + string textContent, IEnumerable mediaFiles, string context, string visibility, bool shouldNotify) + { + PostId = postId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + TextContent = textContent; + MediaFiles = mediaFiles; + Context = context; + Visibility = visibility; + ShouldNotify = shouldNotify; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostStateChanged.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostStateChanged.cs index 623119e63..bcda091b7 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostStateChanged.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostStateChanged.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostUpdated.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostUpdated.cs index e1a6a3d95..7edee43d7 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostUpdated.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostUpdated.cs @@ -1,16 +1,33 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; +using System; +using System.Collections.Generic; namespace MiniSpace.Services.Posts.Application.Events { public class PostUpdated : IEvent { public Guid PostId { get; } - public IEnumerable MediaFilesIds { get; } + public Guid? UserId { get; } + public Guid? OrganizationId { get; } + public Guid? EventId { get; } + public string TextContent { get; } + public IEnumerable MediaFilesUrls { get; } + public string Context { get; } + public string Visibility { get; } + public bool ShouldNotify { get; } - public PostUpdated(Guid postId, IEnumerable mediaFilesIds) + public PostUpdated(Guid postId, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, + IEnumerable mediaFilesUrls, string context, string visibility, bool shouldNotify) { PostId = postId; - MediaFilesIds = mediaFilesIds; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + TextContent = textContent; + MediaFilesUrls = mediaFilesUrls; + Context = context; + Visibility = visibility; + ShouldNotify = shouldNotify; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostsStateUpdated.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostsStateUpdated.cs index 9fe0af0e1..00f6cbcdd 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostsStateUpdated.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/PostsStateUpdated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/ChangePostStateRejected.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/ChangePostStateRejected.cs index b7db43226..6349d93a5 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/ChangePostStateRejected.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/ChangePostStateRejected.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events.Rejected { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/CreatePostRejected.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/CreatePostRejected.cs index 198e1f322..607f1e20b 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/CreatePostRejected.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/CreatePostRejected.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events.Rejected { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/DeletePostRejected.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/DeletePostRejected.cs index b4e5879f7..4ce111208 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/DeletePostRejected.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/DeletePostRejected.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events.Rejected { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/UpdatePostRejected.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/UpdatePostRejected.cs index 883e2d6cb..da1678f9a 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/UpdatePostRejected.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Events/Rejected/UpdatePostRejected.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Events.Rejected { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Extensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Extensions.cs index e75cdd897..727c7df95 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Extensions.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Extensions.cs @@ -1,14 +1,14 @@ using System.Diagnostics.CodeAnalysis; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application { [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/MiniSpace.Services.Posts.Application.csproj b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/MiniSpace.Services.Posts.Application.csproj index c626fc4a6..ee9435221 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/MiniSpace.Services.Posts.Application.csproj +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/MiniSpace.Services.Posts.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetOrganizerPosts.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetOrganizerPosts.cs index 468acc48d..f3dfdf759 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetOrganizerPosts.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetOrganizerPosts.cs @@ -1,6 +1,6 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; namespace MiniSpace.Services.Posts.Application.Queries diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPost.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPost.cs index fde0324d4..280cf9586 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPost.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPost.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; namespace MiniSpace.Services.Posts.Application.Queries diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs index 027485e29..3724e5a25 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetPosts.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Core.Wrappers; using System; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserFeed.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserFeed.cs index aa49850f9..14a7f78e1 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserFeed.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserFeed.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Core.Wrappers; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserPostViews.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserPostViews.cs index 98dbb78cb..c0c25a2fc 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserPostViews.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Queries/GetUserPostViews.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.DTO; using MiniSpace.Services.Posts.Core.Wrappers; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IEventMapper.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IEventMapper.cs index 07815a803..f14dbc7fe 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Posts.Core.Events; namespace MiniSpace.Services.Posts.Application.Services diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IMessageBroker.cs index 8c4c1f5d2..96a83cc10 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Posts.Application.Services { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs index 6f5c9baf8..55a42bc73 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Application/Services/IPostsService.cs @@ -1,4 +1,6 @@ +using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Application.Dto; +using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Requests; using MiniSpace.Services.Posts.Core.Wrappers; @@ -6,11 +8,10 @@ namespace MiniSpace.Services.Posts.Application.Services { public interface IPostsService { - /// - /// Browses posts based on the given request parameters. - /// - /// The browsing request containing filtering, sorting, and pagination information. - /// A paged response containing the posts matching the criteria. Task> BrowsePostsAsync(BrowseRequest request); + Task CreatePostAsync(CreatePost command); + Task UpdatePostAsync(UpdatePost command); + Task RepostPostAsync(RepostCommand command); + Task DeletePostAsync(DeletePost command); } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PageOwnerType.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PageOwnerType.cs new file mode 100644 index 000000000..49e3526be --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PageOwnerType.cs @@ -0,0 +1,8 @@ +namespace MiniSpace.Services.Posts.Core.Entities +{ + public enum PageOwnerType + { + User, + Organization + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs index 2870b48f0..ab11de598 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/Post.cs @@ -12,17 +12,30 @@ public class Post : AggregateRoot public Guid? OrganizationId { get; private set; } public Guid? EventId { get; private set; } public string TextContent { get; private set; } - public IEnumerable MediaFiles { get; private set; } + public IEnumerable MediaFiles { get; private set; } public State State { get; private set; } public DateTime? PublishDate { get; private set; } public DateTime CreatedAt { get; private set; } public DateTime? UpdatedAt { get; private set; } public PostContext Context { get; private set; } - public VisibilityStatus Visibility { get; private set; } + public VisibilityStatus Visibility { get; private set; } + public PostType Type { get; private set; } + public string Title { get; private set; } + // New fields to track the page ownership + public Guid? PageOwnerId { get; private set; } // Owner of the page where the post is published (could be User or Organization) + public PageOwnerType PageOwnerType { get; private set; } // Specifies if the page belongs to a user or an organization + + // New fields to track reposts + public Guid? OriginalPostId { get; private set; } + public bool IsRepost => OriginalPostId.HasValue; + + // Constructor public Post(Guid id, Guid? userId, Guid? organizationId, Guid? eventId, string textContent, IEnumerable mediaFiles, DateTime createdAt, State state, PostContext context, DateTime? publishDate, - VisibilityStatus visibility = VisibilityStatus.Visible, DateTime? updatedAt = null) + PostType type, string title = null, VisibilityStatus visibility = VisibilityStatus.Visible, + DateTime? updatedAt = null, Guid? pageOwnerId = null, PageOwnerType pageOwnerType = PageOwnerType.User, + Guid? originalPostId = null) { Id = id; UserId = userId; @@ -35,11 +48,74 @@ public Post(Guid id, Guid? userId, Guid? organizationId, Guid? eventId, string t State = state; PublishDate = publishDate; Context = context; + Type = type; + Title = title; Visibility = visibility; + PageOwnerId = pageOwnerId; + PageOwnerType = pageOwnerType; + OriginalPostId = originalPostId; AddEvent(new PostCreatedEvent(Id)); } + // Factory methods for different types of posts + + public static Post CreateBlogPost(Guid id, Guid userId, string title, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, + VisibilityStatus visibility = VisibilityStatus.Visible, Guid? pageOwnerId = null, PageOwnerType pageOwnerType = PageOwnerType.User) + { + CheckTextContent(id, textContent, PostType.BlogPost); + + return new Post(id, userId, null, null, textContent, mediaFiles, createdAt, state, PostContext.UserPage, + publishDate ?? createdAt, PostType.BlogPost, title, visibility, updatedAt: null, pageOwnerId: pageOwnerId, pageOwnerType: pageOwnerType); + } + + public static Post CreateSocialPost(Guid id, Guid userId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, + VisibilityStatus visibility = VisibilityStatus.Visible, Guid? pageOwnerId = null, PageOwnerType pageOwnerType = PageOwnerType.User) + { + CheckTextContent(id, textContent, PostType.SocialPost); + + return new Post(id, userId, null, null, textContent, mediaFiles, createdAt, state, PostContext.UserPage, + publishDate ?? createdAt, PostType.SocialPost, title: null, visibility, updatedAt: null, pageOwnerId: pageOwnerId, pageOwnerType: pageOwnerType); + } + + public static Post CreateOrganizationPost(Guid id, Guid userId, Guid organizationId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, + VisibilityStatus visibility = VisibilityStatus.Visible) + { + CheckTextContent(id, textContent, PostType.SocialPost); + + return new Post(id, userId, organizationId, null, textContent, mediaFiles, createdAt, state, PostContext.OrganizationPage, + publishDate ?? createdAt, PostType.SocialPost, title: null, visibility, updatedAt: null, pageOwnerId: organizationId, pageOwnerType: PageOwnerType.Organization); + } + + public static Post CreateEventPost(Guid id, Guid userId, Guid organizationId, Guid eventId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, + VisibilityStatus visibility = VisibilityStatus.Visible) + { + CheckTextContent(id, textContent, PostType.SocialPost); + + return new Post(id, userId, organizationId, eventId, textContent, mediaFiles, createdAt, state, PostContext.EventPage, + publishDate ?? createdAt, PostType.SocialPost, title: null, visibility, updatedAt: null, pageOwnerId: organizationId, pageOwnerType: PageOwnerType.Organization); + } + + // Factory method for repost + public static Post CreateRepost(Guid id, Guid userId, Post originalPost, DateTime createdAt, State state) + { + if (originalPost == null) + { + throw new InvalidPostStateException("Original post cannot be null."); + } + + return new Post(id, userId, null, originalPost.EventId, originalPost.TextContent, originalPost.MediaFiles, + createdAt, state, originalPost.Context, publishDate: createdAt, originalPost.Type, + originalPost.Title, originalPost.Visibility, pageOwnerId: userId, pageOwnerType: PageOwnerType.User, + originalPostId: originalPost.Id); + } + + // Methods to manage post state + public void SetToBePublished(DateTime publishDate, DateTime now) { CheckPublishDate(Id, State.ToBePublished, publishDate, now); @@ -81,7 +157,7 @@ public void SetVisibility(VisibilityStatus visibility, DateTime now) AddEvent(new PostVisibilityChangedEvent(Id, visibility, now)); } - + // Update the state based on the current time public bool UpdateState(DateTime now) { if (State == State.ToBePublished && PublishDate <= now) @@ -117,37 +193,9 @@ public void ChangeState(State newState, DateTime? publishDate, DateTime now) } } - public static Post CreateForUser(Guid id, Guid userId, string textContent, - IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility = VisibilityStatus.Visible) - { - CheckTextContent(id, textContent); - - return new Post(id, userId, null, null, textContent, mediaFiles, createdAt, state, PostContext.UserPage, - publishDate ?? createdAt, visibility); - } - - public static Post CreateForOrganization(Guid id, Guid organizationId, Guid? userId, string textContent, - IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility = VisibilityStatus.Visible) - { - CheckTextContent(id, textContent); - - return new Post(id, userId, organizationId, null, textContent, mediaFiles, createdAt, state, PostContext.OrganizationPage, - publishDate ?? createdAt, visibility); - } - - public static Post CreateForEvent(Guid id, Guid eventId, Guid? userId, Guid? organizationId, string textContent, - IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility = VisibilityStatus.Visible) - { - CheckTextContent(id, textContent); - - return new Post(id, userId, organizationId, eventId, textContent, mediaFiles, createdAt, state, PostContext.EventPage, - publishDate ?? createdAt, visibility); - } - - public void Update(string textContent, IEnumerable mediaFiles, DateTime now) { - CheckTextContent(Id, textContent); + CheckTextContent(Id, textContent, Type); TextContent = textContent; MediaFiles = mediaFiles; @@ -165,14 +213,27 @@ public void RemoveMediaFile(string mediaFileUrl, DateTime now) MediaFiles = MediaFiles.Where(mf => mf != mediaFileUrl).ToList(); UpdatedAt = now; + } + // Repost the post + public void Repost(Guid userId, DateTime now) + { + if (IsRepost) + { + throw new InvalidPostStateException("Cannot repost a repost."); + } + + AddEvent(new PostRepostedEvent(Id, userId, OriginalPostId ?? Id, now)); } - private static void CheckTextContent(AggregateId id, string textContent) + // Validation Methods + private static void CheckTextContent(AggregateId id, string textContent, PostType postType) { - if (string.IsNullOrWhiteSpace(textContent) || textContent.Length > 5000) + int maxTextLength = postType == PostType.BlogPost ? 40000 : 5000; + + if (string.IsNullOrWhiteSpace(textContent) || textContent.Length > maxTextLength) { - throw new InvalidPostTextContentException(id); + throw new InvalidPostTextContentException(id, maxTextLength); } } @@ -183,5 +244,27 @@ private static void CheckPublishDate(AggregateId id, State state, DateTime publi throw new InvalidPostPublishDateException(id, state, publishDate, now); } } + + public static Post CreateForUser(Guid id, Guid userId, string textContent, IEnumerable mediaFiles, + DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility) + { + return new Post(id, userId, null, null, textContent, mediaFiles, createdAt, state, PostContext.UserPage, + publishDate ?? createdAt, PostType.SocialPost, visibility: visibility, pageOwnerId: userId, pageOwnerType: PageOwnerType.User); + } + + public static Post CreateForOrganization(Guid id, Guid organizationId, Guid? userId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility) + { + return new Post(id, userId, organizationId, null, textContent, mediaFiles, createdAt, state, PostContext.OrganizationPage, + publishDate ?? createdAt, PostType.SocialPost, visibility: visibility, pageOwnerId: organizationId, pageOwnerType: PageOwnerType.Organization); + } + + public static Post CreateForEvent(Guid id, Guid eventId, Guid? userId, Guid? organizationId, string textContent, + IEnumerable mediaFiles, DateTime createdAt, State state, DateTime? publishDate, VisibilityStatus visibility) + { + return new Post(id, userId, organizationId, eventId, textContent, mediaFiles, createdAt, state, PostContext.EventPage, + publishDate ?? createdAt, PostType.SocialPost, visibility: visibility, pageOwnerId: organizationId ?? userId, pageOwnerType: organizationId.HasValue ? PageOwnerType.Organization : PageOwnerType.User); + } } } + diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PostType.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PostType.cs new file mode 100644 index 000000000..07a1078c9 --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Entities/PostType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace MiniSpace.Services.Posts.Core.Entities +{ + public enum PostType + { + BlogPost, + SocialPost + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostRepostedEvent.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostRepostedEvent.cs new file mode 100644 index 000000000..3b866767a --- /dev/null +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Events/PostRepostedEvent.cs @@ -0,0 +1,20 @@ +using System; + +namespace MiniSpace.Services.Posts.Core.Events +{ + public class PostRepostedEvent : IDomainEvent + { + public Guid PostId { get; } + public Guid UserId { get; } + public Guid OriginalPostId { get; } + public DateTime RepostedAt { get; } + + public PostRepostedEvent(Guid postId, Guid userId, Guid originalPostId, DateTime repostedAt) + { + PostId = postId; + UserId = userId; + OriginalPostId = originalPostId; + RepostedAt = repostedAt; + } + } +} diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/InvalidPostTextContentException.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/InvalidPostTextContentException.cs index dc6d794ac..bfc4aa612 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/InvalidPostTextContentException.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Core/Exceptions/InvalidPostTextContentException.cs @@ -4,11 +4,13 @@ public class InvalidPostTextContentException : DomainException { public override string Code { get; } = "invalid_post_text_content"; public Guid Id { get; } - - public InvalidPostTextContentException(Guid id) : base( - $"Post with id: {id} has invalid content. Its length should be between 1 and 5000 characters.") + public int MaxTextLength { get; } + + public InvalidPostTextContentException(Guid id, int maxTextLength) + : base($"Post with id: '{id}' has invalid content. The length should be between 1 and {maxTextLength} characters.") { Id = id; + MaxTextLength = maxTextLength; } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Contexts/AppContextFactory.cs index 65b8aed07..beb57cf05 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Posts.Application; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 240cacbc4..084dddffd 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,8 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Posts.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index ac562b6ed..7d1b7485d 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,8 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Posts.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index caa07a45b..99735a958 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Application.Events.Rejected; using MiniSpace.Services.Posts.Application.Exceptions; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 42e3230dd..95de3c068 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,8 +1,8 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Core.Exceptions; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs index 1e96b74bc..6629b63b8 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -44,13 +44,14 @@ using MiniSpace.Services.Posts.Application.Services.Clients; using Microsoft.ML; using MiniSpace.Services.Events.Infrastructure.Mongo.Repositories; +using Paralax.CQRS.WebApi; namespace MiniSpace.Services.Posts.Infrastructure { [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddSingleton(); @@ -109,7 +110,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/Extensions.cs index a1d2c95aa..b1d1e7d5f 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/Extensions.cs @@ -1,15 +1,15 @@ using System.Diagnostics.CodeAnalysis; -using Convey; -using Convey.Logging.CQRS; +using Paralax; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Posts.Application.Commands; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Posts.Infrastructure.Logging { [ExcludeFromCodeCoverage] internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(UpdatePost).Assembly; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs index c67be8004..7315b4c1e 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,8 +1,8 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Logging.CQRS; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Application.Events; using MiniSpace.Services.Posts.Application.Events.External; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Posts.Infrastructure.Logging { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/MiniSpace.Services.Posts.Infrastructure.csproj b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/MiniSpace.Services.Posts.Infrastructure.csproj index f7e40f538..e65c20f40 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/MiniSpace.Services.Posts.Infrastructure.csproj +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/MiniSpace.Services.Posts.Infrastructure.csproj @@ -6,35 +6,45 @@ disable - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/CommentDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/CommentDocument.cs index f88b51164..72eefcd80 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/CommentDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/CommentDocument.cs @@ -1,5 +1,5 @@ using System; -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs index 18f83a151..9e09019c6 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/Extensions.cs @@ -8,21 +8,24 @@ namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents public static class Extensions { public static Post AsEntity(this PostDocument document) - => new Post( - document.Id, - document.UserId, - document.OrganizationId, - document.EventId, - document.TextContent, - document.MediaFiles, - document.CreatedAt, - document.State, - document.Context, - document.PublishDate, - document.Visibility, - document.UpdatedAt); - - + => new Post( + document.Id, + document.UserId, + document.OrganizationId, + document.EventId, + document.TextContent, + document.MediaFiles, + document.CreatedAt, + document.State, + document.Context, + document.PublishDate, + document.Type, + document.Title, + document.Visibility, + document.UpdatedAt, + document.PageOwnerId, + document.PageOwnerType, + document.OriginalPostId); public static PostDocument AsDocument(this Post entity) => new PostDocument() @@ -38,7 +41,12 @@ public static PostDocument AsDocument(this Post entity) State = entity.State, PublishDate = entity.PublishDate, Context = entity.Context, - Visibility = entity.Visibility + Visibility = entity.Visibility, + Type = entity.Type, + Title = entity.Title, + PageOwnerId = entity.PageOwnerId, + PageOwnerType = entity.PageOwnerType, + OriginalPostId = entity.OriginalPostId }; public static PostDto AsDto(this PostDocument document) @@ -54,7 +62,10 @@ public static PostDto AsDto(this PostDocument document) UpdatedAt = document.UpdatedAt, State = document.State.ToString().ToLowerInvariant(), PublishDate = document.PublishDate, - Visibility = document.Visibility.ToString().ToLowerInvariant() + Visibility = document.Visibility.ToString().ToLowerInvariant(), + PageOwnerId = document.PageOwnerId, + PageOwnerType = document.PageOwnerType.ToString().ToLowerInvariant(), + OriginalPostId = document.OriginalPostId }; public static PostDto AsDto(this Post post) @@ -71,7 +82,10 @@ public static PostDto AsDto(this Post post) UpdatedAt = post.UpdatedAt, State = post.State.ToString().ToLowerInvariant(), PublishDate = post.PublishDate, - Visibility = post.Visibility.ToString().ToLowerInvariant() + Visibility = post.Visibility.ToString().ToLowerInvariant(), + PageOwnerId = post.PageOwnerId, + PageOwnerType = post.PageOwnerType.ToString().ToLowerInvariant(), + OriginalPostId = post.OriginalPostId }; } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostDocument.cs index 79de08c61..41db02d06 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationEventPostDocument.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostDocument.cs index 3aabf819d..3ac83374b 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/OrganizationPostDocument.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs index ab49a1ca6..97135c1b0 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/PostDocument.cs @@ -1,7 +1,8 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Posts.Core.Entities; using System; +using System.Collections.Generic; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents { @@ -11,14 +12,24 @@ public class PostDocument : IIdentifiable public Guid Id { get; set; } public Guid? EventId { get; set; } public Guid? UserId { get; set; } - public Guid? OrganizationId { get; set; } + public Guid? OrganizationId { get; set; } public string TextContent { get; set; } public IEnumerable MediaFiles { get; set; } public State State { get; set; } public DateTime? PublishDate { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } - public PostContext Context { get; set; } - public VisibilityStatus Visibility { get; set; } + public PostContext Context { get; set; } + public VisibilityStatus Visibility { get; set; } + public PostType Type { get; set; } + public string Title { get; set; } + + // New fields to track page ownership + public Guid? PageOwnerId { get; set; } // Owner of the page where the post is published (User or Organization) + public PageOwnerType PageOwnerType { get; set; } // Specifies if the page belongs to a user or an organization + + // New field to track reposts + public Guid? OriginalPostId { get; set; } // Reference to the original post if this is a repost + public bool IsRepost => OriginalPostId.HasValue; // Indicates if the post is a repost } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserCommentsDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserCommentsDocument.cs index bdd649a2c..a6313e170 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserCommentsDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserCommentsDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MongoDB.Bson.Serialization.Attributes; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostDocument.cs index ebff8b3c9..9f3eea094 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserEventPostDocument.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostDocument.cs index 97dd514e3..4b63dfb2b 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostDocument.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostsViewsDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostsViewsDocument.cs index 923215369..04b4db5f5 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostsViewsDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserPostsViewsDocument.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Posts.Core.Entities; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserReactionDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserReactionDocument.cs index 190bef22e..ad66a0d39 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserReactionDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/UserReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MongoDB.Bson.Serialization.Attributes; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/ViewDocument.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/ViewDocument.cs index 279f3aa25..8eaf12ccf 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/ViewDocument.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Documents/ViewDocument.cs @@ -1,5 +1,5 @@ using System; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Posts.Core.Entities; namespace MiniSpace.Services.Posts.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs index 82e083af5..262360445 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetOrganizerPostsHandler.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Posts.Application; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Queries; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostHandler.cs index 35a56b605..d583b832c 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostHandler.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Queries; using MiniSpace.Services.Posts.Core.Repositories; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs index 4abe231ed..dd6725827 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetPostsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Queries; using MiniSpace.Services.Posts.Application.Services; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserFeedHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserFeedHandler.cs index 2d0ed7d7f..97a737d00 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserFeedHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserFeedHandler.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Queries; using MiniSpace.Services.Posts.Application.Services; @@ -44,10 +44,15 @@ public GetUserFeedHandler( public async Task> HandleAsync(GetUserFeed query, CancellationToken cancellationToken) { - _logger.LogInformation("Handling GetUserFeed query: {Query}", JsonConvert.SerializeObject(query)); + Console.WriteLine($"Query Parameters - UserId: {query.UserId}, PageNumber: {query.PageNumber}, PageSize: {query.PageSize}, SortBy: {query.SortBy}, Direction: {query.Direction}"); + + var user = await _studentsServiceClient.GetStudentByIdAsync(query.UserId); + // var serializedUser = JsonConvert.SerializeObject(user, Formatting.Indented); + // Console.WriteLine($"Retrieved User Object: {serializedUser}"); + var allPostsRequest = new BrowseRequest { SortBy = new List { query.SortBy }, @@ -56,6 +61,9 @@ public async Task> HandleAsync(GetUserFeed query, Cancell var allPostsResult = await _postsService.BrowsePostsAsync(allPostsRequest); + // var serializedAllPostsResult = JsonConvert.SerializeObject(allPostsResult, Formatting.Indented); + // Console.WriteLine($"Retrieved All posts Object: {serializedAllPostsResult}"); + if (allPostsResult == null || !allPostsResult.Items.Any()) { return new PagedResponse(Enumerable.Empty(), query.PageNumber, query.PageSize, 0); @@ -80,10 +88,20 @@ public async Task> HandleAsync(GetUserFeed query, Cancell var combinedPosts = CombineRankedAndUnrankedPosts(rankedPosts, allPostsResult.Items); - var pagedPosts = combinedPosts - .Skip((query.PageNumber - 1) * query.PageSize) - .Take(query.PageSize) - .ToList(); +// var serializedCombinedPosts = JsonConvert.SerializeObject(combinedPosts, Formatting.Indented); + +// // Output the serialized result to the console +// Console.WriteLine($"Combined Posts Object: {serializedCombinedPosts}"); + + var totalPosts = combinedPosts.Count(); +_logger.LogInformation("Total posts: {TotalPosts}, PageNumber: {PageNumber}, PageSize: {PageSize}, Skip: {Skip}", + totalPosts, query.PageNumber, query.PageSize, (query.PageNumber - 1) * query.PageSize); + +var pagedPosts = combinedPosts + .Skip((query.PageNumber - 1) * query.PageSize) + .Take(query.PageSize) + .ToList(); + _logger.LogInformation("User {UserId} feed generated with {PostCount} posts.", query.UserId, pagedPosts.Count); diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserPostViewsHandler.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserPostViewsHandler.cs index f0cda20ee..9a46b8ec7 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserPostViewsHandler.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Queries/Handlers/GetUserPostViewsHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.DTO; using MiniSpace.Services.Posts.Application.Queries; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationEventPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationEventPostMongoRepository.cs index a609cec66..53f568f66 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationEventPostMongoRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationEventPostMongoRepository.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; using MiniSpace.Services.Posts.Core.Requests; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationPostMongoRepository.cs index 5c06fc11c..c54564471 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationPostMongoRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/OrganizationPostMongoRepository.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; using MiniSpace.Services.Posts.Core.Requests; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs index cb6fd7614..5bab38df1 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostMongoRepository.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; using MiniSpace.Services.Posts.Core.Requests; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostsUserViewsMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostsUserViewsMongoRepository.cs index 3a51102d1..b6ea63755 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostsUserViewsMongoRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/PostsUserViewsMongoRepository.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using MongoDB.Driver; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Posts.Core.Repositories; using MiniSpace.Services.Posts.Infrastructure.Mongo.Documents; using MiniSpace.Services.Posts.Core.Entities; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserEventPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserEventPostMongoRepository.cs index fe5b417d1..1973b8f5c 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserEventPostMongoRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserEventPostMongoRepository.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; using MiniSpace.Services.Posts.Core.Requests; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserPostMongoRepository.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserPostMongoRepository.cs index 56d7b5fc1..736211293 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserPostMongoRepository.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Mongo/Repositories/UserPostMongoRepository.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Posts.Core.Entities; using MiniSpace.Services.Posts.Core.Repositories; using MiniSpace.Services.Posts.Core.Requests; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/FriendsServiceClient.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/FriendsServiceClient.cs index 2951f8610..e57936470 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/FriendsServiceClient.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/FriendsServiceClient.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Services.Clients; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/StudentsServiceClient.cs index 41530b2e4..ae2e73887 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Services.Clients; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/EventMapper.cs index d9d99822b..0c4fa5835 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/EventMapper.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Core; using MiniSpace.Services.Posts.Core.Events; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/MessageBroker.cs index b4ed67764..58d7d6fe3 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs index fdbac5102..18c1a8974 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/PostsService.cs @@ -1,4 +1,5 @@ using MiniSpace.Services.Posts.Application; +using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Application.Dto; using MiniSpace.Services.Posts.Application.Exceptions; using MiniSpace.Services.Posts.Application.Services; @@ -15,26 +16,29 @@ namespace MiniSpace.Services.Posts.Infrastructure.Services { public class PostsService : IPostsService { - private readonly IOrganizationPostRepository _organizationPostRepository; - private readonly IOrganizationEventPostRepository _organizationEventPostRepository; + private readonly IPostRepository _postRepository; private readonly IUserPostRepository _userPostRepository; + private readonly IOrganizationPostRepository _organizationPostRepository; private readonly IUserEventPostRepository _userEventPostRepository; - private readonly IPostRepository _postRepository; + private readonly IOrganizationEventPostRepository _organizationEventPostRepository; + private readonly IDateTimeProvider _dateTimeProvider; private readonly IAppContext _appContext; public PostsService( - IOrganizationPostRepository organizationPostRepository, - IOrganizationEventPostRepository organizationEventPostRepository, + IPostRepository postRepository, IUserPostRepository userPostRepository, + IOrganizationPostRepository organizationPostRepository, IUserEventPostRepository userEventPostRepository, - IPostRepository postRepository, + IOrganizationEventPostRepository organizationEventPostRepository, + IDateTimeProvider dateTimeProvider, IAppContext appContext) { - _organizationPostRepository = organizationPostRepository; - _organizationEventPostRepository = organizationEventPostRepository; + _postRepository = postRepository; _userPostRepository = userPostRepository; + _organizationPostRepository = organizationPostRepository; _userEventPostRepository = userEventPostRepository; - _postRepository = postRepository; + _organizationEventPostRepository = organizationEventPostRepository; + _dateTimeProvider = dateTimeProvider; _appContext = appContext; } @@ -101,5 +105,315 @@ public async Task> BrowsePostsAsync(BrowseRequest request pagedResponse.TotalItems ); } + + public async Task CreatePostAsync(CreatePost command) + { + var identity = _appContext.Identity; + + // Check if the user is authorized to create posts on the specified page + if (identity.IsAuthenticated && identity.Id != command.UserId && identity.Id != command.OrganizationId && identity.Id != command.PageOwnerId) + { + throw new UnauthorizedPostCreationAttemptException(identity.Id, command.EventId ?? Guid.Empty); + } + + if (command.PostId == Guid.Empty || await _postRepository.ExistsAsync(command.PostId)) + { + throw new InvalidPostIdException(command.PostId); + } + + if (!Enum.TryParse(command.State, true, out var newState)) + { + throw new InvalidPostStateException(command.State); + } + + if (!Enum.TryParse(command.Visibility, true, out var visibilityStatus)) + { + throw new InvalidVisibilityStatusException(command.Visibility); + } + + var mediaFiles = command.MediaFiles.ToList(); + if (mediaFiles.Count > 12) + { + throw new InvalidNumberOfPostMediaFilesException(command.PostId, mediaFiles.Count); + } + + switch (newState) + { + case State.Reported: + throw new NotAllowedPostStateException(command.PostId, newState); + case State.ToBePublished when command.PublishDate is null: + throw new PublishDateNullException(command.PostId, newState); + } + + Post post; + + // Check where the post is being created based on the context and page ownership + if (command.Context == PostContext.UserPage) + { + // If creating a post on another user's page, use the PageOwnerId + post = Post.CreateForUser(command.PostId, command.PageOwnerId ?? command.UserId.Value, command.TextContent, command.MediaFiles, + _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _userPostRepository.AddAsync(post); + } + else if (command.Context == PostContext.OrganizationPage) + { + // If creating a post on an organization page, use the PageOwnerId for the organization + post = Post.CreateForOrganization(command.PostId, command.PageOwnerId ?? command.OrganizationId.Value, command.UserId, command.TextContent, command.MediaFiles, + _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _organizationPostRepository.AddAsync(post); + } + else if (command.Context == PostContext.EventPage) + { + // Handle event posts + if (command.UserId.HasValue) + { + post = Post.CreateForEvent(command.PostId, command.EventId.Value, command.UserId, command.OrganizationId, command.TextContent, + command.MediaFiles, _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _userEventPostRepository.AddAsync(post); + } + else + { + post = Post.CreateForEvent(command.PostId, command.EventId.Value, null, command.OrganizationId, command.TextContent, + command.MediaFiles, _dateTimeProvider.Now, newState, command.PublishDate, visibilityStatus); + await _organizationEventPostRepository.AddAsync(post); + } + } + else + { + throw new InvalidPostContextException(command.Context.ToString()); + } + + return post; + } + + public async Task UpdatePostAsync(UpdatePost command) + { + Post post = null; + + // Fetch the correct post based on context (User, Organization, Event) + if (command.Context == PostContext.UserPage) + { + post = await _userPostRepository.GetAsync(command.PostId); + } + else if (command.Context == PostContext.OrganizationPage) + { + post = await _organizationPostRepository.GetAsync(command.PostId); + } + else if (command.Context == PostContext.EventPage) + { + if (command.UserId.HasValue) + { + post = await _userEventPostRepository.GetAsync(command.PostId); + } + else + { + post = await _organizationEventPostRepository.GetAsync(command.PostId); + } + } + + if (post is null) + { + throw new PostNotFoundException(command.PostId); + } + + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != post.UserId && identity.Id != post.OrganizationId && !identity.IsAdmin) + { + throw new UnauthorizedPostAccessException(command.PostId, identity.Id); + } + + if (!identity.IsAdmin && post.State == State.Reported) + { + throw new UnauthorizedPostOperationException(command.PostId, identity.Id); + } + + // Parse and validate the state and visibility + State? newState = null; + if (!string.IsNullOrWhiteSpace(command.State)) + { + if (!Enum.TryParse(command.State, true, out var parsedState)) + { + throw new InvalidPostStateException(command.State); + } + newState = parsedState; + } + + VisibilityStatus? newVisibility = null; + if (!string.IsNullOrWhiteSpace(command.Visibility)) + { + if (!Enum.TryParse(command.Visibility, true, out var parsedVisibility)) + { + throw new InvalidVisibilityStatusException(command.Visibility); + } + newVisibility = parsedVisibility; + } + + // Check and validate media files + var mediaFiles = command.MediaFiles.ToList(); + if (mediaFiles.Count > 12) + { + throw new InvalidNumberOfPostMediaFilesException(post.Id, mediaFiles.Count); + } + + // Update the post's text content, media, and state + post.Update(command.TextContent, mediaFiles, _dateTimeProvider.Now); + + if (newState.HasValue) + { + post.ChangeState(newState.Value, command.PublishDate, _dateTimeProvider.Now); + } + + if (newVisibility.HasValue) + { + post.SetVisibility(newVisibility.Value, _dateTimeProvider.Now); + } + + // Update the post in the correct repository + if (command.Context == PostContext.UserPage) + { + await _userPostRepository.UpdateAsync(post); + } + else if (command.Context == PostContext.OrganizationPage) + { + await _organizationPostRepository.UpdateAsync(post); + } + else if (command.Context == PostContext.EventPage) + { + if (command.UserId.HasValue) + { + await _userEventPostRepository.UpdateAsync(post); + } + else + { + await _organizationEventPostRepository.UpdateAsync(post); + } + } + + return post; + } + + public async Task RepostPostAsync(RepostCommand command) + { + Post originalPost = null; + + // Fetch the original post based on context + if (command.Context == PostContext.UserPage) + { + originalPost = await _userPostRepository.GetAsync(command.OriginalPostId); + } + else if (command.Context == PostContext.OrganizationPage) + { + originalPost = await _organizationPostRepository.GetAsync(command.OriginalPostId); + } + else if (command.Context == PostContext.EventPage) + { + if (command.UserId.HasValue) + { + originalPost = await _userEventPostRepository.GetAsync(command.OriginalPostId); + } + else + { + originalPost = await _organizationEventPostRepository.GetAsync(command.OriginalPostId); + } + } + + if (originalPost is null) + { + throw new PostNotFoundException(command.OriginalPostId); + } + + // Create the repost based on the original post + var repost = Post.CreateRepost( + command.RepostedPostId, + command.PageOwnerId ?? command.UserId ?? Guid.Empty, // Reposting by user or page owner + originalPost, + _dateTimeProvider.Now, + State.Published + ); + + // Add repost to the correct repository based on context + if (command.Context == PostContext.UserPage) + { + await _userPostRepository.AddAsync(repost); + } + else if (command.Context == PostContext.OrganizationPage) + { + await _organizationPostRepository.AddAsync(repost); + } + else if (command.Context == PostContext.EventPage) + { + if (command.UserId.HasValue) + { + await _userEventPostRepository.AddAsync(repost); + } + else + { + await _organizationEventPostRepository.AddAsync(repost); + } + } + + return repost; + } + + public async Task DeletePostAsync(DeletePost command) + { + Post post = null; + + // Determine where to delete the post based on the context (user page, org page, or event page) + switch (command.Context.ToLowerInvariant()) + { + case "userpage": + post = await _userPostRepository.GetAsync(command.PostId); + break; + case "organizationpage": + post = await _organizationPostRepository.GetAsync(command.PostId); + break; + case "eventpage" when command.UserId.HasValue: + post = (await _userEventPostRepository.GetByUserEventIdAsync(command.UserId.Value, command.EventId.Value)) + .FirstOrDefault(p => p.Id == command.PostId); + break; + case "eventpage" when command.OrganizationId.HasValue: + post = (await _organizationEventPostRepository.GetByOrganizationEventIdAsync(command.OrganizationId.Value, command.EventId.Value)) + .FirstOrDefault(p => p.Id == command.PostId); + break; + default: + throw new InvalidPostContextException(command.Context); + } + + if (post == null) + { + throw new PostNotFoundException(command.PostId); + } + + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != (post.UserId ?? post.OrganizationId) && !identity.IsAdmin) + { + throw new UnauthorizedPostAccessException(command.PostId, identity.Id); + } + + if (!identity.IsAdmin && post.State == State.Reported) + { + throw new UnauthorizedPostOperationException(command.PostId, identity.Id); + } + + // Perform the deletion + switch (command.Context.ToLowerInvariant()) + { + case "userpage": + await _userPostRepository.DeleteAsync(command.PostId); + break; + case "organizationpage": + await _organizationPostRepository.DeleteAsync(command.PostId); + break; + case "eventpage" when command.UserId.HasValue: + await _userEventPostRepository.DeleteAsync(command.PostId); + break; + case "eventpage" when command.OrganizationId.HasValue: + await _organizationEventPostRepository.DeleteAsync(command.PostId); + break; + default: + throw new InvalidPostContextException(command.Context); + } + } } } diff --git a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Workers/PostStateUpdaterWorker.cs b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Workers/PostStateUpdaterWorker.cs index f0d0b579e..c07976812 100644 --- a/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Workers/PostStateUpdaterWorker.cs +++ b/MiniSpace.Services.Posts/src/MiniSpace.Services.Posts.Infrastructure/Services/Workers/PostStateUpdaterWorker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.Extensions.Hosting; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Application.Events; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/ChangePostStateHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/ChangePostStateHandlerTest.cs index f258b39ec..7fb238a7e 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/ChangePostStateHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/ChangePostStateHandlerTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Posts.Application.Commands.Handlers; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/CreatePostHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/CreatePostHandlerTest.cs index 2467ffb5c..8d7762739 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/CreatePostHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/CreatePostHandlerTest.cs @@ -12,7 +12,7 @@ using MiniSpace.Services.Posts.Application.Commands.Handlers; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/DeletePostHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/DeletePostHandlerTest.cs index d04250ff5..0ae99ca89 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/DeletePostHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/DeletePostHandlerTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Posts.Application.Commands.Handlers; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostHandlerTest.cs index d3a7e19bb..92b63e102 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostHandlerTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Posts.Application.Commands.Handlers; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostsStateHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostsStateHandlerTest.cs index 999294dc8..e033ce6f7 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostsStateHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Commands/Handlers/UpdatePostsStateHandlerTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Posts.Application.Commands.Handlers; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs index 466256943..a8adbfbc7 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs @@ -15,7 +15,7 @@ using MiniSpace.Services.Posts.Application.Events.External.Handlers; using MiniSpace.Services.Posts.Application.Events.External; using System.ComponentModel.Design; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Posts.Application.UnitTests.Events.External.Handlers { diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventDeletedHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventDeletedHandlerTest.cs index 052f51a10..301eae46f 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventDeletedHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/EventDeletedHandlerTest.cs @@ -15,7 +15,7 @@ using MiniSpace.Services.Posts.Application.Events.External.Handlers; using MiniSpace.Services.Posts.Application.Events.External; using System.ComponentModel.Design; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Posts.Application.UnitTests.Events.External.Handlers { diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/MediaFileDeletedHandlerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/MediaFileDeletedHandlerTest.cs index ea872a77d..647ebd8d4 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/MediaFileDeletedHandlerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Application.UnitTests/Events/External/Handlers/MediaFileDeletedHandlerTest.cs @@ -15,7 +15,7 @@ using MiniSpace.Services.Posts.Application.Events.External.Handlers; using MiniSpace.Services.Posts.Application.Events.External; using System.ComponentModel.Design; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Posts.Core.Exceptions; namespace MiniSpace.Services.Posts.Application.UnitTests.Events.External.Handlers diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/AggregatedIdTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/AggregatedIdTest.cs index baf52192c..53eff02ab 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/AggregatedIdTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/AggregatedIdTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Posts.Application.Commands.Handlers; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/PostTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/PostTest.cs index 4c0c2658d..eee3853c5 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/PostTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Core.UnitTests/Entities/PostTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Posts.Application.Commands.Handlers; using MiniSpace.Services.Posts.Application.Commands; using MiniSpace.Services.Posts.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/MessageBrokerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/MessageBrokerTest.cs index 243ee9a1b..b23488f1c 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/MessageBrokerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/MessageBrokerTest.cs @@ -1,12 +1,12 @@ using Xunit; using Moq; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Collections.Generic; using System.Threading.Tasks; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/PostsServiceTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/PostsServiceTest.cs index c13dfdb90..710d8a11e 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/PostsServiceTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/PostsServiceTest.cs @@ -1,12 +1,12 @@ using Xunit; using Moq; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Collections.Generic; using System.Threading.Tasks; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/Workers/PostStateUpdaterWorkerTest.cs b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/Workers/PostStateUpdaterWorkerTest.cs index 2258332b0..d0c2a40e7 100644 --- a/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/Workers/PostStateUpdaterWorkerTest.cs +++ b/MiniSpace.Services.Posts/tests/MiniSpace.Services.Posts.Infrastructure.UnitTests/Services/Workers/PostStateUpdaterWorkerTest.cs @@ -1,12 +1,12 @@ using Xunit; using Moq; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Collections.Generic; using System.Threading.Tasks; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; @@ -14,7 +14,7 @@ using MiniSpace.Services.Posts.Application.Services; using MiniSpace.Services.Posts.Application.Events; using MiniSpace.Services.Posts.Infrastructure.Services.Workers; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.VisualStudio.TestPlatform.Common.Utilities; using App.Metrics.Timer; using MiniSpace.Services.Posts.Application.Commands; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/MiniSpace.Services.Reactions.Api.csproj b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/MiniSpace.Services.Reactions.Api.csproj index 806f65406..2a83f12b0 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/MiniSpace.Services.Reactions.Api.csproj +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/MiniSpace.Services.Reactions.Api.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/Program.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/Program.cs index 2d0f580b2..b28e51681 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/Program.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Api/Program.cs @@ -1,21 +1,19 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; - using MiniSpace.Services.Reactions.Application; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Application.Dto; using MiniSpace.Services.Reactions.Application.Queries; -using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Infrastructure; +using Paralax.Core; namespace MiniSpace.Services.Reactions.Api { @@ -24,7 +22,7 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs index 0f8cc01fb..8f424cb1f 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/CreateReaction.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Application.Commands diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/DeleteReaction.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/DeleteReaction.cs index 79d3c8297..08eb97e1f 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/DeleteReaction.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/DeleteReaction.cs @@ -1,5 +1,5 @@ using System.Reflection.Metadata; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using Microsoft.AspNetCore.Mvc.TagHelpers; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs index 9f39e64c0..2435ef903 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/CreateReactionHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reactions.Application.Events; using MiniSpace.Services.Reactions.Application.Exceptions; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs index 6c7e85348..20d449532 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/DeleteReactionHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reactions.Application.Events; using MiniSpace.Services.Reactions.Application.Exceptions; using MiniSpace.Services.Reactions.Application.Services; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/UpdateReactionHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/UpdateReactionHandler.cs index 2abec7466..581ef52b1 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/UpdateReactionHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/Handlers/UpdateReactionHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reactions.Application.Exceptions; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/UpdateReaction.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/UpdateReaction.cs index 985807695..efdca4aa5 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/UpdateReaction.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Commands/UpdateReaction.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Reactions.Application.Commands { diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionCreated.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionCreated.cs index facdcb81c..6172eb21d 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionCreated.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Reactions.Application.Events diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionDeleted.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionDeleted.cs index 23358e8d3..761e511e8 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionDeleted.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/ReactionDeleted.cs @@ -1,5 +1,5 @@ using System.Net.Mime; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.Connections; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/AddReactionRejected.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/AddReactionRejected.cs index 2eae6a5cd..6c7b4c7a2 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/AddReactionRejected.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/AddReactionRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.Mvc.RazorPages; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/DeleteReactionRejected.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/DeleteReactionRejected.cs index fe5e476e1..187f80c4c 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/DeleteReactionRejected.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Events/Rejected/DeleteReactionRejected.cs @@ -1,6 +1,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.AspNetCore.Mvc.RazorPages; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Extensions.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Extensions.cs index 85882d7b2..43f948617 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Extensions.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Extensions.cs @@ -1,14 +1,14 @@ using System.Diagnostics.CodeAnalysis; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reactions.Application { [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/MiniSpace.Services.Reactions.Application.csproj b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/MiniSpace.Services.Reactions.Application.csproj index 655099070..4f699d9ec 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/MiniSpace.Services.Reactions.Application.csproj +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/MiniSpace.Services.Reactions.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactions.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactions.cs index 30289cde7..0b39331fd 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactions.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactions.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Reactions.Application.Dto; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactionsSummary.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactionsSummary.cs index a60d50a42..016fd2a72 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactionsSummary.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Queries/GetReactionsSummary.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Reactions.Application.Dto; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Exceptions; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IEventMapper.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IEventMapper.cs index 65cbe1c90..4e8e94d2d 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Reactions.Core.Events; namespace MiniSpace.Services.Reactions.Application.Services diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IMessageBroker.cs index dc95763b7..2b6d71e65 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reactions.Application.Services { diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/AppContextFactory.cs index a743b55df..4b443fe47 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Reactions.Application; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 6448d1944..289a71b42 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,8 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Reactions.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 9c8d7755f..9d06eb5d8 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,8 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Reactions.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index ac5154c53..b3fa0ca0e 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Application.Events.Rejected; using MiniSpace.Services.Reactions.Application.Exceptions; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 45c8017b9..39d04d661 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,8 +1,8 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Reactions.Application.Exceptions; using MiniSpace.Services.Reactions.Core.Exceptions; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs index 296a30ac0..6e2c63f02 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -38,19 +38,19 @@ using MiniSpace.Services.Reactions.Infrastructure.Mongo.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Services; using MiniSpace.Services.Reactions.Application.Queries; -using Convey.Logging.CQRS; using MiniSpace.Services.Reactions.Application.Events; using System.Diagnostics.CodeAnalysis; using MiniSpace.Services.Reactions.Application.Services.Clients; using MiniSpace.Services.Reactions.Infrastructure.Services.Clients; +using Paralax.CQRS.WebApi; namespace MiniSpace.Services.Reactions.Infrastructure { [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -109,7 +109,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/Extensions.cs index 64466eb7b..a13a55452 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/Extensions.cs @@ -1,15 +1,15 @@ using System.Diagnostics.CodeAnalysis; -using Convey; -using Convey.Logging.CQRS; +using Paralax; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Reactions.Application.Commands; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Reactions.Infrastructure.Logging { [ExcludeFromCodeCoverage] internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { // TODO: Posts had only UpdatePost var assemblyCreate = typeof(CreateReaction).Assembly; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs index b81e4e018..f5027c56f 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Logging.CQRS; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Application.Events; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Reactions.Infrastructure.Logging { diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/MiniSpace.Services.Reactions.Infrastructure.csproj b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/MiniSpace.Services.Reactions.Infrastructure.csproj index c81a53625..fd39a55f3 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/MiniSpace.Services.Reactions.Infrastructure.csproj +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/MiniSpace.Services.Reactions.Infrastructure.csproj @@ -7,29 +7,34 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventCommentsReactionDocument.cs index 7b965999e..c12a3530d 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventCommentsReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventCommentsReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventReactionDocument.cs index 9bfbde392..d14faef98 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationEventReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostCommentsReactionDocument.cs index 2e285f98b..2b8e27d54 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostCommentsReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostCommentsReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostReactionDocument.cs index c050c9077..980e64722 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/OrganizationPostReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs index 3508b1563..d65d96618 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/ReactionDocument.cs @@ -1,5 +1,5 @@ using System; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventCommentsReactionDocument.cs index d8e9a99c0..44efe21de 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventCommentsReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventCommentsReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventReactionDocument.cs index dc6b60523..569c793ed 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserEventReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostCommentsReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostCommentsReactionDocument.cs index 7f831c097..5622189bb 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostCommentsReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostCommentsReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostReactionDocument.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostReactionDocument.cs index 8acd4aba2..e783901c8 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostReactionDocument.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Documents/UserPostReactionDocument.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reactions.Core.Entities; namespace MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs index 7792cbbdd..cd724c30a 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Reactions.Application.Dto; using MiniSpace.Services.Reactions.Application.Queries; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs index 652eb63d5..adeac192c 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Queries/Handlers/GetReactionsSummaryHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Reactions.Application; using MiniSpace.Services.Reactions.Application.Dto; using MiniSpace.Services.Reactions.Application.Queries; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs index 518900901..f8ca9a2f3 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionMongoRepository.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using Jaeger.Propagation; using Microsoft.AspNetCore.Components.Forms; using MiniSpace.Services.Reactions.Core.Entities; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventCommentsMongoRepository.cs index 94373d7f4..d6e082f71 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventCommentsMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventCommentsMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventMongoRepository.cs index ab76870f4..eb2137f93 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsEventMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostCommentsMongoRepository.cs index 897a1c769..215c94126 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostCommentsMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostCommentsMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostMongoRepository.cs index ba3c10d71..0e9197259 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsOrganizationsPostMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventCommentsMongoRepository.cs index aad68a002..103080cb9 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventCommentsMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventCommentsMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventMongoRepository.cs index 94787f938..6e3762f7f 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserEventMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostCommentsMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostCommentsMongoRepository.cs index ca207a328..8f7d711ec 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostCommentsMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostCommentsMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostMongoRepository.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostMongoRepository.cs index ec55ed5bc..8744ad9fc 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostMongoRepository.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Mongo/Repositories/ReactionsUserPostMongoRepository.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reactions.Core.Entities; using MiniSpace.Services.Reactions.Core.Repositories; using MiniSpace.Services.Reactions.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/CommentsServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/CommentsServiceClient.cs index 2c85a503e..54fe1b8d9 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/CommentsServiceClient.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/CommentsServiceClient.cs @@ -1,6 +1,6 @@ using System; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Reactions.Application.Services.Clients; namespace MiniSpace.Services.Reactions.Infrastructure.Services.Clients diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/StudentsServiceClient.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/StudentsServiceClient.cs index 8ba32121d..f182b0561 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/StudentsServiceClient.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/Clients/StudentsServiceClient.cs @@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; -using Convey.HTTP; +using Paralax.HTTP; using MiniSpace.Services.Reactions.Application.Services.Clients; namespace MiniSpace.Services.Reactions.Infrastructure.Services.Clients diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/EventMapper.cs index 5bf270fb7..54d2dea99 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/EventMapper.cs @@ -1,5 +1,5 @@ using System.Diagnostics.CodeAnalysis; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Reactions.Application.Services; using MiniSpace.Services.Reactions.Core; using MiniSpace.Services.Reactions.Core.Events; diff --git a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/MessageBroker.cs index 3c8a76256..a2c9b8689 100644 --- a/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Reactions/src/MiniSpace.Services.Reactions.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/ChangeReactionHandlerTest.cs b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/ChangeReactionHandlerTest.cs index f3db45b04..2fea93aec 100644 --- a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/ChangeReactionHandlerTest.cs +++ b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/ChangeReactionHandlerTest.cs @@ -12,7 +12,7 @@ using MiniSpace.Services.Reactions.Application.Commands.Handlers; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/DeleteReactionHandlerTest.cs b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/DeleteReactionHandlerTest.cs index a5d12916b..61a08df2f 100644 --- a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/DeleteReactionHandlerTest.cs +++ b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Commands/Handlers/DeleteReactionHandlerTest.cs @@ -12,7 +12,7 @@ using MiniSpace.Services.Reactions.Application.Commands.Handlers; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs index de740e93f..d76d2d12f 100644 --- a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs +++ b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/EventCreatedHandlerTest.cs @@ -15,7 +15,7 @@ using MiniSpace.Services.Reactions.Application.Events.External.Handlers; using MiniSpace.Services.Reactions.Application.Events.External; using System.ComponentModel.Design; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Reactions.Application.UnitTests.Events.External.Handlers { diff --git a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/PostCreatedHandlerTest.cs b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/PostCreatedHandlerTest.cs index e62a753ac..f1dab715a 100644 --- a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/PostCreatedHandlerTest.cs +++ b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Application.UnitTests/Events/External/Handlers/PostCreatedHandlerTest.cs @@ -15,7 +15,7 @@ using MiniSpace.Services.Reactions.Application.Events.External.Handlers; using MiniSpace.Services.Reactions.Application.Events.External; using System.ComponentModel.Design; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reactions.Application.Events; namespace MiniSpace.Services.Reactions.Application.UnitTests.Events.External.Handlers diff --git a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Core.UnitTests/Entities/AggregatedIdTest.cs b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Core.UnitTests/Entities/AggregatedIdTest.cs index dd1209cda..01fdbdb8b 100644 --- a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Core.UnitTests/Entities/AggregatedIdTest.cs +++ b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Core.UnitTests/Entities/AggregatedIdTest.cs @@ -13,7 +13,7 @@ using MiniSpace.Services.Reactions.Application.Commands.Handlers; using MiniSpace.Services.Reactions.Application.Commands; using MiniSpace.Services.Reactions.Infrastructure.Contexts; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System.Threading; using System.Security.Claims; using FluentAssertions; diff --git a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Infrastructure.UnitTests/Services/MessageBrokerTest.cs b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Infrastructure.UnitTests/Services/MessageBrokerTest.cs index dcd755ff6..640627200 100644 --- a/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Infrastructure.UnitTests/Services/MessageBrokerTest.cs +++ b/MiniSpace.Services.Reactions/tests/MiniSpace.Services.Reactions.Infrastructure.UnitTests/Services/MessageBrokerTest.cs @@ -1,12 +1,12 @@ using Xunit; using Moq; -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; using System.Collections.Generic; using System.Threading.Tasks; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/MiniSpace.Services.Reports.Api.csproj b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/MiniSpace.Services.Reports.Api.csproj index 9ae62c447..2da1cf5e8 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/MiniSpace.Services.Reports.Api.csproj +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/MiniSpace.Services.Reports.Api.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/Program.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/Program.cs index a1ee78d14..605959c62 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/Program.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Api/Program.cs @@ -1,10 +1,8 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.Logging; +using Paralax.WebApi; using Microsoft.AspNetCore; using MiniSpace.Services.Reports.Application; using MiniSpace.Services.Reports.Application.Commands; @@ -13,6 +11,8 @@ using MiniSpace.Services.Reports.Application.Services; using MiniSpace.Services.Reports.Core.Wrappers; using MiniSpace.Services.Reports.Infrastructure; +using Paralax.CQRS.WebApi; +using Paralax.Core; namespace MiniSpace.Services.Reports.Api { @@ -21,22 +21,18 @@ public class Program public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) .ConfigureServices(services => services - .AddConvey() + .AddParalax() .AddWebApi() .AddApplication() .AddInfrastructure() .Build()) .Configure(app => app .UseInfrastructure() - .UseEndpoints(endpoints => endpoints - .Post("reports/search", async (cmd, ctx) => - { - var pagedResult = await ctx.RequestServices.GetService().BrowseReportsAsync(cmd); - await ctx.Response.WriteJsonAsync(pagedResult); - }) - ) .UseDispatcherEndpoints(endpoints => endpoints .Get("", ctx => ctx.Response.WriteAsync(ctx.RequestServices.GetService().Name)) + + .Get>("reports/search") + .Post("reports", afterDispatch: (cmd, ctx) => ctx.Response.Created($"reports/{cmd.ReportId}")) .Delete("reports/{reportId}") @@ -44,7 +40,7 @@ public static async Task Main(string[] args) .Post("reports/{reportId}/start-review") .Post("reports/{reportId}/resolve") .Post("reports/{reportId}/reject") - .Get>>("reports/students/{studentId}") + .Get>("reports/students/{studentId}") )) .UseLogging() .Build() diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CancelReport.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CancelReport.cs index a054fb34c..2d8eb035d 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CancelReport.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CancelReport.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Reports.Application.Commands { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CreateReport.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CreateReport.cs index 5e7759caf..44efc89a6 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CreateReport.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/CreateReport.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reports.Core.Entities; namespace MiniSpace.Services.Reports.Application.Commands diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/DeleteReport.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/DeleteReport.cs index 9d028fb6a..b090fa2e9 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/DeleteReport.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/DeleteReport.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Reports.Application.Commands { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CancelReportHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CancelReportHandler.cs index 58309c9c8..3830f8ba6 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CancelReportHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CancelReportHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reports.Application.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Application.Services; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CreateReportHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CreateReportHandler.cs index 53de5e340..8ee0b00dc 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CreateReportHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/CreateReportHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reports.Application.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Application.Services; @@ -36,7 +36,7 @@ public async Task HandleAsync(CreateReport command, CancellationToken cancellati var contextType = _reportValidator.ParseContextType(command.ContextType); var category = _reportValidator.ParseCategory(command.Category); _reportValidator.ValidateReason(command.Reason); - var activeStudentReports = await _reportRepository.GetStudentActiveReportsAsync(command.IssuerId); + var activeStudentReports = await _reportRepository.GetUserActiveReportsAsync(command.IssuerId); _reportValidator.ValidateActiveReports(activeStudentReports.Count()); var report = Report.Create(command.ReportId, command.IssuerId, command.TargetId, command.TargetOwnerId, diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/DeleteReportHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/DeleteReportHandler.cs index 6df5ee548..f4b11617f 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/DeleteReportHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/DeleteReportHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reports.Application.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Application.Services; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/RejectReportHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/RejectReportHandler.cs index 7d1234dff..3fee1aad0 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/RejectReportHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/RejectReportHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reports.Application.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Application.Services; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/ResolveReportHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/ResolveReportHandler.cs index d35caae8a..45fc41556 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/ResolveReportHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/ResolveReportHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reports.Application.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Application.Services; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/StartReportReviewHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/StartReportReviewHandler.cs index 610d27192..6418f14d3 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/StartReportReviewHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/Handlers/StartReportReviewHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Reports.Application.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Application.Services; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/RejectReport.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/RejectReport.cs index e5b47ca22..5c227c51d 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/RejectReport.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/RejectReport.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Reports.Application.Commands { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/ResolveReport.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/ResolveReport.cs index 304ac20f6..9ed7dbbd6 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/ResolveReport.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/ResolveReport.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Reports.Application.Commands { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/SearchReports.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/SearchReports.cs deleted file mode 100644 index 388e493e9..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/SearchReports.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Convey.CQRS.Commands; -using MiniSpace.Services.Reports.Application.DTO; - -namespace MiniSpace.Services.Reports.Application.Commands -{ - public class SearchReports : ICommand - { - public IEnumerable ContextTypes { get; set; } - public IEnumerable States { get; set; } - public Guid ReviewerId { get; set; } - public PageableDto Pageable { get; set; } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/StartReportReview.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/StartReportReview.cs index e585861a4..6bc071c9b 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/StartReportReview.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Commands/StartReportReview.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Reports.Application.Commands { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/CommentCreated.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/CommentCreated.cs index c10c6a5e1..9ef2703c3 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/CommentCreated.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/CommentCreated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Reports.Application.Events.External { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/EventCreated.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/EventCreated.cs index 0945bf7c1..961307e59 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/EventCreated.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/EventCreated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Reports.Application.Events.External { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/CommentCreatedHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/CommentCreatedHandler.cs index fcf2834f9..07ef11f52 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/CommentCreatedHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/CommentCreatedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/EventCreatedHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/EventCreatedHandler.cs index 2ca301504..d63e3b9cb 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/EventCreatedHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/EventCreatedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/PostCreatedHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/PostCreatedHandler.cs index e6e793880..07690ab3d 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/PostCreatedHandler.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/PostCreatedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/StudentCreatedHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/StudentCreatedHandler.cs deleted file mode 100644 index a286aded7..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/Handlers/StudentCreatedHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Convey.CQRS.Events; -using MiniSpace.Services.Reports.Application.Exceptions; -using MiniSpace.Services.Reports.Core.Entities; -using MiniSpace.Services.Reports.Core.Repositories; - -namespace MiniSpace.Services.Reports.Application.Events.External.Handlers -{ - public class StudentCreatedHandler : IEventHandler - { - private readonly IStudentRepository _studentRepository; - - public StudentCreatedHandler(IStudentRepository studentRepository) - { - _studentRepository = studentRepository; - } - - public async Task HandleAsync(StudentCreated @event, CancellationToken cancellationToken) - { - if (await _studentRepository.ExistsAsync(@event.StudentId)) - { - throw new StudentAlreadyAddedException(@event.StudentId); - } - - await _studentRepository.AddAsync(new Student(@event.StudentId)); - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/PostCreated.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/PostCreated.cs index a94c9a727..f220fd5c8 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/PostCreated.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/PostCreated.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Reports.Application.Events.External { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/StudentCreated.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/StudentCreated.cs index 2cbad872e..7a79bba4d 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/StudentCreated.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/External/StudentCreated.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Reports.Application.Events.External { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCancelled.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCancelled.cs index 32f463725..50f4ab1af 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCancelled.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCancelled.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application.Events { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCreated.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCreated.cs index d44f5b863..d450a0a23 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCreated.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application.Events { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportDeleted.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportDeleted.cs index 370c5e200..9066eeb7d 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportDeleted.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application.Events { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportRejected.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportRejected.cs index bf63a3126..e06280db8 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportRejected.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application.Events { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportResolved.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportResolved.cs index 92f7272f7..1e6cd8b65 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportResolved.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportResolved.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application.Events { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportReviewStarted.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportReviewStarted.cs index 345af8682..6925a95c7 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportReviewStarted.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Events/ReportReviewStarted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application.Events { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Extensions.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Extensions.cs index 790a11250..1a20cf962 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Extensions.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Extensions.cs @@ -1,12 +1,12 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application { public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/MiniSpace.Services.Reports.Application.csproj b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/MiniSpace.Services.Reports.Application.csproj index f6e6a64af..3bebd1308 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/MiniSpace.Services.Reports.Application.csproj +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/MiniSpace.Services.Reports.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/GetStudentReports.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/GetStudentReports.cs deleted file mode 100644 index 66ce81dab..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/GetStudentReports.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Convey.CQRS.Queries; -using MiniSpace.Services.Reports.Application.DTO; -using MiniSpace.Services.Reports.Core.Wrappers; - -namespace MiniSpace.Services.Reports.Application.Queries -{ - public class GetStudentReports : IQuery>> - { - public Guid StudentId { get; set; } - public int Page { get; set; } - public int Results { get; set; } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/GetUserReports.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/GetUserReports.cs new file mode 100644 index 000000000..9dffd114b --- /dev/null +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/GetUserReports.cs @@ -0,0 +1,18 @@ +using Paralax.CQRS.Queries; +using MiniSpace.Services.Reports.Application.DTO; +using MiniSpace.Services.Reports.Core.Wrappers; +using System; + +namespace MiniSpace.Services.Reports.Application.Queries +{ + public class GetUserReports : IQuery> + { + public Guid UserId { get; set; } + public int Page { get; set; } = 1; + public int Results { get; set; } = 10; + public string SortBy { get; set; } + public string Direction { get; set; } + public IEnumerable ContextTypes { get; set; } + public IEnumerable States { get; set; } + } +} diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/SearchReports.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/SearchReports.cs new file mode 100644 index 000000000..690f0f14b --- /dev/null +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Queries/SearchReports.cs @@ -0,0 +1,20 @@ +using Paralax.CQRS.Queries; +using MiniSpace.Services.Reports.Application.DTO; +using MiniSpace.Services.Reports.Core.Wrappers; +using System; +using System.Collections.Generic; + +namespace MiniSpace.Services.Reports.Application.Queries +{ + public class SearchReports : IQuery> + { + public IEnumerable ContextTypes { get; set; } + public IEnumerable States { get; set; } + public Guid ReviewerId { get; set; } + + public int Page { get; set; } = 1; + public int Size { get; set; } = 10; + public string SortBy { get; set; } + public string Direction { get; set; } + } +} diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IEventMapper.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IEventMapper.cs index 3d420a78f..31a661cab 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Reports.Core.Events; namespace MiniSpace.Services.Reports.Application.Services diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IMessageBroker.cs index 7db19df2f..3510b0e75 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Reports.Application.Services { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IReportsService.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IReportsService.cs index bc97204a2..7fae83331 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IReportsService.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Application/Services/IReportsService.cs @@ -1,11 +1,12 @@ using MiniSpace.Services.Reports.Application.Commands; using MiniSpace.Services.Reports.Application.DTO; +using MiniSpace.Services.Reports.Application.Queries; using MiniSpace.Services.Reports.Core.Wrappers; namespace MiniSpace.Services.Reports.Application.Services { public interface IReportsService { - Task>> BrowseReportsAsync(SearchReports command); + Task> BrowseReportsAsync(SearchReports query); } } \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Entities/ContextType.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Entities/ContextType.cs index 56818d8c3..71bbfbc1d 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Entities/ContextType.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Entities/ContextType.cs @@ -5,6 +5,6 @@ public enum ContextType Event, Post, Comment, - StudentProfile + UserProfile } } \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Entities/Student.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Entities/Student.cs deleted file mode 100644 index daa9eaad5..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Entities/Student.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace MiniSpace.Services.Reports.Core.Entities -{ - public class Student(Guid id) - { - public Guid Id { get; private set; } = id; - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/MiniSpace.Services.Reports.Core.sln b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/MiniSpace.Services.Reports.Core.sln new file mode 100644 index 000000000..639f2592a --- /dev/null +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/MiniSpace.Services.Reports.Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Reports.Core", "MiniSpace.Services.Reports.Core.csproj", "{552B2140-6159-4844-9319-0DB786CD835F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {552B2140-6159-4844-9319-0DB786CD835F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {552B2140-6159-4844-9319-0DB786CD835F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {552B2140-6159-4844-9319-0DB786CD835F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {552B2140-6159-4844-9319-0DB786CD835F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7FCC4904-B191-4C78-8B6A-6A3C5D38FFB8} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Repositories/IReportRepository.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Repositories/IReportRepository.cs index a9bdf907b..c90047a89 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Repositories/IReportRepository.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Repositories/IReportRepository.cs @@ -5,14 +5,14 @@ namespace MiniSpace.Services.Reports.Core.Repositories public interface IReportRepository { Task GetAsync(Guid id); - Task> GetStudentActiveReportsAsync(Guid studentId); + Task> GetUserActiveReportsAsync(Guid userId); Task AddAsync(Report report); Task UpdateAsync(Report report); Task DeleteAsync(Guid id); Task<(IEnumerable reports, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseReportsAsync( int pageNumber, int pageSize, IEnumerable contextTypes, IEnumerable states, - Guid reviewerId, IEnumerable sortBy, string direction); - Task<(IEnumerable reports, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseStudentReportsAsync( - int pageNumber, int pageSize, Guid studentId, IEnumerable sortBy, string direction); + Guid reviewerId, string sortBy, string direction); + Task<(IEnumerable reports, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseUserReportsAsync( + int pageNumber, int pageSize, Guid userId, string sortBy, string direction); } } \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Repositories/IStudentRepository.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Repositories/IStudentRepository.cs deleted file mode 100644 index fb1a98fd0..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Repositories/IStudentRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Threading.Tasks; -using MiniSpace.Services.Reports.Core.Entities; - -namespace MiniSpace.Services.Reports.Core.Repositories -{ - public interface IStudentRepository - { - Task GetAsync(Guid id); - Task ExistsAsync(Guid id); - Task AddAsync(Student student); - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Wrappers/PagedResponse.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Wrappers/PagedResponse.cs index 61d33a008..79c73f10d 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Wrappers/PagedResponse.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Core/Wrappers/PagedResponse.cs @@ -1,28 +1,32 @@ -namespace MiniSpace.Services.Reports.Core.Wrappers +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MiniSpace.Services.Reports.Core.Wrappers { - public class PagedResponse : Response + public class PagedResponse { + public IEnumerable Items { get; } public int TotalPages { get; } - public int TotalElements { get; } - public int Size { get; } - public int Number { get; } + public int TotalItems { get; } + public int PageSize { get; } + public int Page { get; } public bool First { get; } public bool Last { get; } public bool Empty { get; } + public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null; + public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null; - public PagedResponse(T content, int pageNumber, int pageSize, int totalPages, int totalElements) + public PagedResponse(IEnumerable items, int page, int pageSize, int totalItems) { - Content = content; - TotalPages = totalPages; - TotalElements = totalElements; - Size = pageSize; - Number = pageNumber; - First = pageNumber == 0; - Last = pageNumber == totalPages - 1; - Empty = totalElements == 0; - Succeeded = true; - Errors = null; - Message = null; + Items = items; + PageSize = pageSize; + TotalItems = totalItems; + TotalPages = pageSize > 0 ? (int)Math.Ceiling((decimal)totalItems / pageSize) : 0; + Page = page; + First = page == 1; + Last = page == TotalPages; + Empty = !items.Any(); } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Contexts/AppContextFactory.cs index 30907d2eb..32efd48a7 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Reports.Application; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 3400f96a2..880883337 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; namespace MiniSpace.Services.Reports.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 6687e433e..c254ff761 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; namespace MiniSpace.Services.Reports.Infrastructure.Decorators { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 7c568efe0..801a8eb2a 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; // using MiniSpace.Services.Reports.Application.Commands; // using MiniSpace.Services.Reports.Application.Events.Rejected; // using MiniSpace.Services.Reports.Application.Events.External; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index d40597dfb..095d65ae5 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,7 +1,7 @@ using System.Collections.Concurrent; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Reports.Application.Exceptions; using MiniSpace.Services.Reports.Core.Exceptions; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Extensions.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Extensions.cs index f07933ed6..9403d9066 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -39,19 +39,19 @@ using MiniSpace.Services.Reports.Infrastructure.Mongo.Documents; using MiniSpace.Services.Reports.Infrastructure.Mongo.Repositories; using MiniSpace.Services.Reports.Infrastructure.Services; +using Paralax.CQRS.WebApi; namespace MiniSpace.Services.Reports.Infrastructure { public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); - builder.Services.AddTransient(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -80,7 +80,6 @@ public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) .AddMongoRepository("events") .AddMongoRepository("posts") .AddMongoRepository("comments") - .AddMongoRepository("students") .AddWebApiSwaggerDocs() .AddCertificateAuthentication() .AddSecurity(); @@ -91,7 +90,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/Extensions.cs index 7efe1fbab..6ec03fd68 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/Extensions.cs @@ -1,13 +1,13 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Reports.Application.Commands; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Reports.Infrastructure.Logging { internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(CreateReport).Assembly; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/MessageToLogTemplateMapper.cs index b72c79ceb..d082d7549 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,6 +1,6 @@ -using Convey.Logging.CQRS; using MiniSpace.Services.Reports.Application.Commands; using MiniSpace.Services.Reports.Application.Events.External; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Reports.Infrastructure.Logging { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/MiniSpace.Services.Reports.Infrastructure.csproj b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/MiniSpace.Services.Reports.Infrastructure.csproj index e116eb0bb..b54eac0db 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/MiniSpace.Services.Reports.Infrastructure.csproj +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/MiniSpace.Services.Reports.Infrastructure.csproj @@ -7,29 +7,34 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/MiniSpace.Services.Reports.Infrastructure.sln b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/MiniSpace.Services.Reports.Infrastructure.sln new file mode 100644 index 000000000..81c1cc2a3 --- /dev/null +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/MiniSpace.Services.Reports.Infrastructure.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MiniSpace.Services.Reports.Infrastructure", "MiniSpace.Services.Reports.Infrastructure.csproj", "{338E5162-1709-449C-9F2D-75ABA3B8AF5A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {338E5162-1709-449C-9F2D-75ABA3B8AF5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {338E5162-1709-449C-9F2D-75ABA3B8AF5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {338E5162-1709-449C-9F2D-75ABA3B8AF5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {338E5162-1709-449C-9F2D-75ABA3B8AF5A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {11B44C44-15B3-4162-A1F0-0B43E8B8D23D} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/CommentDocument.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/CommentDocument.cs index b27bbd5db..34b8f1e1e 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/CommentDocument.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/CommentDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/EventDocument.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/EventDocument.cs index 5f8363c13..b9a48e339 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/EventDocument.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/EventDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/Extensions.cs index 70953c80d..0f1b4d41c 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/Extensions.cs @@ -68,14 +68,5 @@ public static CommentDocument AsDocument(this Comment entity) { Id = entity.Id, }; - - public static StudentDocument AsDocument(this Student entity) - => new () - { - Id = entity.Id, - }; - - public static Student AsEntity(this StudentDocument document) - => new (document.Id); } } \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/PostDocument.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/PostDocument.cs index 1db0c2a6c..96b17bf37 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/PostDocument.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/PostDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Documents { diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/ReportDocument.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/ReportDocument.cs index b198c1d59..25d4596d0 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/ReportDocument.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/ReportDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Reports.Core.Entities; namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/StudentDocument.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/StudentDocument.cs deleted file mode 100644 index e910924f8..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Documents/StudentDocument.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Convey.Types; - -namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Documents -{ - public class StudentDocument: IIdentifiable - { - public Guid Id { get; set; } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Queries/Handlers/GetStudentReportsHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Queries/Handlers/GetStudentReportsHandler.cs deleted file mode 100644 index 5af0f0339..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Queries/Handlers/GetStudentReportsHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Convey.CQRS.Queries; -using MiniSpace.Services.Reports.Application; -using MiniSpace.Services.Reports.Application.DTO; -using MiniSpace.Services.Reports.Application.Queries; -using MiniSpace.Services.Reports.Core.Repositories; -using MiniSpace.Services.Reports.Core.Wrappers; - -namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Queries.Handlers -{ - public class GetStudentReportsHandler : IQueryHandler>> - { - private readonly IReportRepository _reportRepository; - private readonly IAppContext _appContext; - - public GetStudentReportsHandler(IReportRepository reportRepository, IAppContext appContext) - { - _reportRepository = reportRepository; - _appContext = appContext; - } - - public async Task>> HandleAsync(GetStudentReports query, CancellationToken cancellationToken) - { - var identity = _appContext.Identity; - if (identity.IsAuthenticated && identity.Id != query.StudentId) - { - return new PagedResponse>(Enumerable.Empty(), - 1, query.Results, 0, 0); - } - - var result = await _reportRepository.BrowseStudentReportsAsync(query.Page, query.Results, - query.StudentId, Enumerable.Empty(), "dsc"); - - return new PagedResponse>(result.reports.Select(r => new ReportDto(r)), - result.pageNumber, result.pageSize, result.totalPages, result.totalElements); - } - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Queries/Handlers/GetUserReportsHandler.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Queries/Handlers/GetUserReportsHandler.cs new file mode 100644 index 000000000..3f99e8041 --- /dev/null +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Queries/Handlers/GetUserReportsHandler.cs @@ -0,0 +1,37 @@ +using Paralax.CQRS.Queries; +using MiniSpace.Services.Reports.Application; +using MiniSpace.Services.Reports.Application.DTO; +using MiniSpace.Services.Reports.Application.Queries; +using MiniSpace.Services.Reports.Core.Repositories; +using MiniSpace.Services.Reports.Core.Wrappers; + +namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Queries.Handlers +{ + public class GetUserReportsHandler : IQueryHandler> + { + private readonly IReportRepository _reportRepository; + private readonly IAppContext _appContext; + + public GetUserReportsHandler(IReportRepository reportRepository, IAppContext appContext) + { + _reportRepository = reportRepository; + _appContext = appContext; + } + + public async Task> HandleAsync(GetUserReports query, CancellationToken cancellationToken) + { + var identity = _appContext.Identity; + if (identity.IsAuthenticated && identity.Id != query.UserId) + { + return new PagedResponse(Enumerable.Empty(), + query.Page, query.Results, 0); + } + + var result = await _reportRepository.BrowseUserReportsAsync( + query.Page, query.Results, query.UserId, query.SortBy, query.Direction); + + var reports = result.reports.Select(r => new ReportDto(r)); + return new PagedResponse(reports, result.pageNumber, result.pageSize, result.totalElements); + } + } +} diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs index cc4f1b775..48caa5045 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/CommentMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; using MiniSpace.Services.Reports.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/EventMongoRepository.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/EventMongoRepository.cs index 05dd8abab..c0562be78 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/EventMongoRepository.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/EventMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; using MiniSpace.Services.Reports.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/Extensions.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/Extensions.cs index 12dfc1689..e8fc15924 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/Extensions.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/Extensions.cs @@ -8,6 +8,7 @@ namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Repositories public static class Extensions { private static readonly FilterDefinitionBuilder FilterDefinitionBuilder = Builders.Filter; + public static async Task<(int totalPages, int totalElements, IReadOnlyList data)> AggregateByPage( this IMongoCollection collection, FilterDefinition filterDefinition, @@ -29,7 +30,6 @@ public static class Extensions PipelineStageDefinitionBuilder.Limit(pageSize), })); - var aggregation = await collection.Aggregate() .Match(filterDefinition) .Facet(countFacet, dataFacet) @@ -45,8 +45,8 @@ public static class Extensions { return (0, 0, Array.Empty()); } + var totalPages = (int)Math.Ceiling((double)count / pageSize); - var data = aggregation.First() .Facets.First(x => x.Name == "data") .Output(); @@ -56,33 +56,29 @@ public static class Extensions public static FilterDefinition ToFilterDefinition() { - var filterDefinition = FilterDefinitionBuilder.Empty; - - return filterDefinition; + return FilterDefinitionBuilder.Empty; } - - public static FilterDefinition AddContextTypesFilter (this FilterDefinition filterDefinition, - IEnumerable contextTypesEnumerable) + + public static FilterDefinition AddContextTypesFilter(this FilterDefinition filterDefinition, IEnumerable contextTypes) { - var contextTypes = contextTypesEnumerable.ToList(); - if(contextTypes.Any()) + var contextTypesList = contextTypes.ToList(); + if (contextTypesList.Any()) { - filterDefinition &= FilterDefinitionBuilder.In(x => x.ContextType, contextTypes); + filterDefinition &= FilterDefinitionBuilder.In(x => x.ContextType, contextTypesList); } return filterDefinition; } - - public static FilterDefinition AddStatesFilter (this FilterDefinition filterDefinition, - IEnumerable statesEnumerable) + + public static FilterDefinition AddStatesFilter(this FilterDefinition filterDefinition, IEnumerable states) { - var states = statesEnumerable.ToList(); - if(states.Count != 0) + var statesList = states.ToList(); + if (statesList.Any()) { - filterDefinition &= FilterDefinitionBuilder.In(x => x.State, states); + filterDefinition &= FilterDefinitionBuilder.In(x => x.State, statesList); } return filterDefinition; } - + public static FilterDefinition AddReviewerIdFilter(this FilterDefinition filterDefinition, Guid reviewerId) { if (reviewerId != Guid.Empty) @@ -91,27 +87,27 @@ public static FilterDefinition AddReviewerIdFilter(this FilterDe } return filterDefinition; } - + public static FilterDefinition AddStudentIdFilter(this FilterDefinition filterDefinition, Guid studentId) { filterDefinition &= FilterDefinitionBuilder.Eq(x => x.IssuerId, studentId); return filterDefinition; } - public static SortDefinition ToSortDefinition(IEnumerable sortByArguments, string direction) + public static SortDefinition ToSortDefinition(string sortBy, string direction) { - var sort = sortByArguments.ToList(); - if(sort.Count == 0) + var sortDefinitionBuilder = Builders.Sort; + + if (string.IsNullOrWhiteSpace(sortBy)) { - sort.Add("UpdatedAt"); + sortBy = "UpdatedAt"; } - var sortDefinitionBuilder = Builders.Sort; - var sortDefinition = sort - .Select(sortBy => direction == "asc" - ? sortDefinitionBuilder.Ascending(sortBy) - : sortDefinitionBuilder.Descending(sortBy)); - var sortCombined = sortDefinitionBuilder.Combine(sortDefinition); - return sortCombined; + + var sortDefinition = direction == "asc" + ? sortDefinitionBuilder.Ascending(sortBy) + : sortDefinitionBuilder.Descending(sortBy); + + return sortDefinition; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/PostMongoRepository.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/PostMongoRepository.cs index d2658e569..427141368 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/PostMongoRepository.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/PostMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; using MiniSpace.Services.Reports.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/ReportMongoRepository.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/ReportMongoRepository.cs index 49bf0eeab..5a06bfbe9 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/ReportMongoRepository.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/ReportMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; using MiniSpace.Services.Reports.Infrastructure.Mongo.Documents; @@ -22,9 +22,9 @@ public async Task GetAsync(Guid id) return report?.AsEntity(); } - public async Task> GetStudentActiveReportsAsync(Guid studentId) + public async Task> GetUserActiveReportsAsync(Guid userId) { - var reports = await _repository.FindAsync(r => r.IssuerId == studentId + var reports = await _repository.FindAsync(r => r.IssuerId == userId && (r.State == ReportState.Submitted || r.State == ReportState.UnderReview)); return reports.Select(r => r.AsEntity()); @@ -54,7 +54,7 @@ public Task DeleteAsync(Guid id) public async Task<(IEnumerable reports, int pageNumber,int pageSize, int totalPages, int totalElements)> BrowseReportsAsync(int pageNumber, int pageSize, IEnumerable contextTypes, IEnumerable states, Guid reviewerId, - IEnumerable sortBy, string direction) + string sortBy, string direction) { var filterDefinition = Extensions.ToFilterDefinition() .AddContextTypesFilter(contextTypes) @@ -68,8 +68,8 @@ public Task DeleteAsync(Guid id) pagedEvents.totalPages, pagedEvents.totalElements); } - public async Task<(IEnumerable reports, int pageNumber, int pageSize, int totalPages, int totalElements)> BrowseStudentReportsAsync(int pageNumber, int pageSize, - Guid studentId, IEnumerable sortBy, string direction) + public async Task<(IEnumerable reports, int pageNumber, int pageSize, int totalPages, int totalElements)> BrowseUserReportsAsync(int pageNumber, int pageSize, + Guid studentId, string sortBy, string direction) { var filterDefinition = Extensions.ToFilterDefinition() .AddStudentIdFilter(studentId); diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs deleted file mode 100644 index 2fade5d17..000000000 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Threading.Tasks; -using Convey.Persistence.MongoDB; -using MiniSpace.Services.Reports.Core.Entities; -using MiniSpace.Services.Reports.Core.Repositories; -using MiniSpace.Services.Reports.Infrastructure.Mongo.Documents; - -namespace MiniSpace.Services.Reports.Infrastructure.Mongo.Repositories -{ - public class StudentMongoRepository : IStudentRepository - { - private readonly IMongoRepository _repository; - - public StudentMongoRepository(IMongoRepository repository) - { - _repository = repository; - } - public async Task GetAsync(Guid id) - { - var student = await _repository.GetAsync(s => s.Id == id); - - return student?.AsEntity(); - } - public Task ExistsAsync(Guid id) => _repository.ExistsAsync(s => s.Id == id); - public Task AddAsync(Student student) => _repository.AddAsync(student.AsDocument()); - } -} \ No newline at end of file diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/EventMapper.cs index f171cdbe5..add871166 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Reports.Application.Services; using MiniSpace.Services.Reports.Core; using MiniSpace.Services.Reports.Core.Events; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/MessageBroker.cs index a832264df..945eaaccb 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/ReportsService.cs b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/ReportsService.cs index 771e6b97b..499dd6290 100644 --- a/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/ReportsService.cs +++ b/MiniSpace.Services.Reports/src/MiniSpace.Services.Reports.Infrastructure/Services/ReportsService.cs @@ -1,11 +1,13 @@ using MiniSpace.Services.Reports.Application; -using MiniSpace.Services.Reports.Application.Commands; using MiniSpace.Services.Reports.Application.DTO; using MiniSpace.Services.Reports.Application.Exceptions; +using MiniSpace.Services.Reports.Application.Queries; using MiniSpace.Services.Reports.Application.Services; using MiniSpace.Services.Reports.Core.Entities; using MiniSpace.Services.Reports.Core.Repositories; using MiniSpace.Services.Reports.Core.Wrappers; +using System.Linq; +using System.Threading.Tasks; namespace MiniSpace.Services.Reports.Infrastructure.Services { @@ -22,7 +24,7 @@ public ReportsService(IReportRepository reportRepository, IReportValidator repor _appContext = appContext; } - public async Task>> BrowseReportsAsync(SearchReports command) + public async Task> BrowseReportsAsync(SearchReports query) { var identity = _appContext.Identity; if (identity.IsAuthenticated && !identity.IsAdmin) @@ -30,29 +32,25 @@ public async Task>> BrowseReportsAsync(Sear throw new UnauthorizedReportSearchAttemptException(identity.Id, identity.Role); } - var contextTypes = new List(); - foreach (var contextType in command.ContextTypes) - { - contextTypes.Add(_reportValidator.ParseContextType(contextType)); - } - - var states = new List(); - foreach (var status in command.States) - { - states.Add(_reportValidator.ParseStatus(status)); - } + var contextTypes = query.ContextTypes + .Select(ct => _reportValidator.ParseContextType(ct)) + .ToList(); + + var states = query.States + .Select(status => _reportValidator.ParseStatus(status)) + .ToList(); - var pageNumber = command.Pageable.Page < 1 ? 1 : command.Pageable.Page; - var pageSize = command.Pageable.Size > 10 ? 10 : command.Pageable.Size; + var pageNumber = query.Page < 1 ? 1 : query.Page; + var pageSize = query.Size > 10 ? 10 : query.Size; - var result = await _reportRepository.BrowseReportsAsync(pageNumber, pageSize, - contextTypes, states, command.ReviewerId, command.Pageable.Sort.SortBy, command.Pageable.Sort.Direction); + var result = await _reportRepository.BrowseReportsAsync( + pageNumber, pageSize, contextTypes, states, query.ReviewerId, query.SortBy, query.Direction); - var pagedReports = new PagedResponse>( + var pagedReports = new PagedResponse( result.reports.Select(r => new ReportDto(r)), - result.pageNumber, result.pageSize, result.totalPages, result.totalElements); + result.pageNumber, result.pageSize, result.totalElements); return pagedReports; } } -} \ No newline at end of file +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Grpc/StudentServiceGrpc.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Grpc/StudentServiceGrpc.cs new file mode 100644 index 000000000..1634fb03b --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Grpc/StudentServiceGrpc.cs @@ -0,0 +1,87 @@ +using Grpc.Core; +using MiniSpace.Services.Students.Application.Queries; +using MiniSpace.Services.Students.Application.Dto; +using Paralax.CQRS.Queries; +using System; +using System.Linq; +using System.Threading.Tasks; +using MiniSpace.Services.Students.Grpc; + +namespace MiniSpace.Services.Students.Api.Grpc +{ + public class StudentServiceGrpc : StudentService.StudentServiceBase + { + private readonly IQueryDispatcher _queryDispatcher; + + public StudentServiceGrpc(IQueryDispatcher queryDispatcher) + { + _queryDispatcher = queryDispatcher; + } + + // Implement the GetStudent gRPC method + public override async Task GetStudent(GetStudentRequest request, ServerCallContext context) + { + var studentDto = await _queryDispatcher.QueryAsync(new GetStudent + { + StudentId = Guid.Parse(request.StudentId) + }); + + if (studentDto == null) + { + throw new RpcException(new Status(StatusCode.NotFound, "Student not found")); + } + + return new StudentResponse + { + StudentId = studentDto.Id.ToString(), + FirstName = studentDto.FirstName, + LastName = studentDto.LastName, + Email = studentDto.Email, + ProfileImageUrl = studentDto.ProfileImageUrl, + Description = studentDto.Description + }; + } + + // Implement the GetPaginatedStudents gRPC method + public override async Task GetPaginatedStudents(GetPaginatedStudentsRequest request, ServerCallContext context) + { + // Fetch paginated students using the dispatcher + var paginatedStudents = await _queryDispatcher.QueryAsync>(new GetStudents + { + Page = request.Page, + ResultsPerPage = request.PageSize // Use ResultsPerPage instead of PageSize + }); + + if (paginatedStudents == null || !paginatedStudents.Results.Any()) + { + return new GetPaginatedStudentsResponse + { + TotalCount = 0 + }; + } + + // Create the response with pagination details + var response = new GetPaginatedStudentsResponse + { + TotalCount = paginatedStudents.Total, + PageSize = paginatedStudents.PageSize, + CurrentPage = paginatedStudents.Page, + NextPageUrl = paginatedStudents.NextPage ?? string.Empty, + PrevPageUrl = paginatedStudents.PrevPage ?? string.Empty + }; + + // Map students to the response + response.Students.AddRange(paginatedStudents.Results.Select(s => new StudentResponse + { + StudentId = s.Id.ToString(), + FirstName = s.FirstName, + LastName = s.LastName, + Email = s.Email, + ProfileImageUrl = s.ProfileImageUrl, + Description = s.Description + })); + + return response; + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/MiniSpace.Services.Students.Api.csproj b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/MiniSpace.Services.Students.Api.csproj index 0eb5a6f59..e55c0903c 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/MiniSpace.Services.Students.Api.csproj +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/MiniSpace.Services.Students.Api.csproj @@ -7,14 +7,28 @@ - - - - + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Program.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Program.cs index 97903c2cb..ac7676f73 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Program.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Program.cs @@ -1,21 +1,25 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Convey; -using Convey.CQRS.Queries; -using Convey.Logging; -using Convey.Types; -using Convey.WebApi; -using Convey.WebApi.CQRS; +using Paralax; +using Paralax.CQRS.Queries; +using Paralax.Logging; +using Paralax.Types; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; +using MiniSpace.Services.Students.Api.Grpc; using MiniSpace.Services.Students.Application; using MiniSpace.Services.Students.Application.Commands; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Core.Wrappers; using MiniSpace.Services.Students.Infrastructure; +using Microsoft.AspNetCore.Builder; +using Paralax.CQRS.WebApi; +using Paralax.Core; namespace MiniSpace.Services.Students.Api { @@ -23,12 +27,15 @@ public class Program { public static async Task Main(string[] args) => await WebHost.CreateDefaultBuilder(args) - .ConfigureServices(services => services - .AddConvey() - .AddWebApi() - .AddApplication() - .AddInfrastructure() - .Build()) + .ConfigureServices(services => + { + services.AddParalax() + .AddWebApi() + .AddApplication() + .AddInfrastructure(); + + services.AddGrpc(); + }) .Configure(app => app .UseInfrastructure() .UseDispatcherEndpoints(endpoints => endpoints @@ -41,10 +48,9 @@ public static async Task Main(string[] args) .Get("students/{studentId}/events") .Get("students/{studentId}/notifications") .Get>("students/profiles/users/{userId}/views/paginated") - .Get>("students/profiles/users/{userId}/views/viewed") + .Get>("students/profiles/users/{userId}/views/viewed") .Get>("students/{blockerId}/blocked-users") - - + .Put("students/{studentId}") .Put("students/{studentId}/settings") .Put("students/{studentId}/state/{state}", @@ -52,17 +58,22 @@ public static async Task Main(string[] args) .Put("students/{studentId}/languages-and-interests") .Delete("students/{studentId}") - + .Post("students/{blockerId}/block-user/{blockedUserId}", afterDispatch: (cmd, ctx) => ctx.Response.Ok()) .Post("students/{blockerId}/unblock-user/{blockedUserId}", afterDispatch: (cmd, ctx) => ctx.Response.Ok()) - + .Post("students", - afterDispatch: (cmd, ctx) => ctx.Response.Created($"students/{cmd.StudentId}")) + afterDispatch: (cmd, ctx) => ctx.Response.Created($"students/{cmd.StudentId}")) .Post("students/{studentId}/notifications") - .Post("students/profiles/users/{userProfileId}/view", afterDispatch: (cmd, ctx) => ctx.Response.Ok()) - )) + .Post("students/profiles/users/{userProfileId}/view", afterDispatch: (cmd, ctx) => ctx.Response.Ok()) + ) + + .UseEndpoints(endpoints => + { + endpoints.MapGrpcService(); + })) .UseLogging() .Build() .RunAsync(); diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Protos/student.proto b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Protos/student.proto new file mode 100644 index 000000000..954a72aa5 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Api/Protos/student.proto @@ -0,0 +1,35 @@ +syntax = "proto3"; + +option csharp_namespace = "MiniSpace.Services.Students.Grpc"; + +service StudentService { + rpc GetStudent (GetStudentRequest) returns (StudentResponse); + rpc GetPaginatedStudents (GetPaginatedStudentsRequest) returns (GetPaginatedStudentsResponse); +} + +message GetStudentRequest { + string student_id = 1; +} + +message StudentResponse { + string student_id = 1; + string first_name = 2; + string last_name = 3; + string email = 4; + string profile_image_url = 5; + string description = 6; +} + +message GetPaginatedStudentsRequest { + int32 page = 1; + int32 page_size = 2; +} + +message GetPaginatedStudentsResponse { + repeated StudentResponse students = 1; + int32 total_count = 2; + int32 page_size = 3; + int32 current_page = 4; + string next_page_url = 5; + string prev_page_url = 6; +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/BlockUser.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/BlockUser.cs index c5641193f..5db6c6769 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/BlockUser.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/BlockUser.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Students.Application.Commands diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ChangeStudentState.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ChangeStudentState.cs index 4dcad43d7..cc01518cd 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ChangeStudentState.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ChangeStudentState.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Students.Application.Commands { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/CompleteStudentRegistration.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/CompleteStudentRegistration.cs index 11713de39..bba183707 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/CompleteStudentRegistration.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/CompleteStudentRegistration.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Students.Application.Commands { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/DeleteStudent.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/DeleteStudent.cs index 9e0de1ddd..373c2211a 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/DeleteStudent.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/DeleteStudent.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Students.Application.Commands { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/BlockUserHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/BlockUserHandler.cs index 6a4b53bf7..59118cdbb 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/BlockUserHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/BlockUserHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Entities; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ChangeStudentStateHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ChangeStudentStateHandler.cs index ab26ecf62..8b92d845e 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ChangeStudentStateHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ChangeStudentStateHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Entities; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/CompleteStudentRegistrationHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/CompleteStudentRegistrationHandler.cs index 81c35c177..bb6198f51 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/CompleteStudentRegistrationHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/CompleteStudentRegistrationHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Exceptions; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/DeleteStudentHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/DeleteStudentHandler.cs index 4a2a7a363..319e9c36e 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/DeleteStudentHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/DeleteStudentHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UnblockUserHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UnblockUserHandler.cs index 75ae90a07..24c1bb2d5 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UnblockUserHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UnblockUserHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Entities; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentHandler.cs index 33c6dd68b..60cf8e1a2 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Events; using MiniSpace.Services.Students.Application.Exceptions; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentLanguagesAndInterestsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentLanguagesAndInterestsHandler.cs index 914af9037..2f22448e3 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentLanguagesAndInterestsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateStudentLanguagesAndInterestsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Events; using MiniSpace.Services.Students.Application.Exceptions; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserNotificationPreferencesHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserNotificationPreferencesHandler.cs index 028d0793d..3d5721005 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserNotificationPreferencesHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserNotificationPreferencesHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; using System; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserSettingsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserSettingsHandler.cs index 9fccb7b56..8b8c139aa 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserSettingsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/UpdateUserSettingsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Entities; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ViewUserProfileHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ViewUserProfileHandler.cs index ea4330070..d5fb295cb 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ViewUserProfileHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/Handlers/ViewUserProfileHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; using Microsoft.Extensions.Logging; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UnblockUser.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UnblockUser.cs index f34dc7f3b..7a40ce364 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UnblockUser.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UnblockUser.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Students.Application.Commands diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudent.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudent.cs index ca5f6c45c..8eec6f0d8 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudent.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudent.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Dto; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudentLanguagesAndInterests.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudentLanguagesAndInterests.cs index 012095465..edef35a52 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudentLanguagesAndInterests.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateStudentLanguagesAndInterests.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserGallery.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserGallery.cs index 5e3a49d6c..cfdb99442 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserGallery.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserGallery.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using MiniSpace.Services.Students.Application.Dto; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserNotificationPreferences.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserNotificationPreferences.cs index ade0c770f..a6489237d 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserNotificationPreferences.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserNotificationPreferences.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Students.Application.Commands diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserSettings.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserSettings.cs index 29027f31a..3e3c0075a 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserSettings.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/UpdateUserSettings.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; using System; namespace MiniSpace.Services.Students.Application.Commands diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ViewUserProfile.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ViewUserProfile.cs index 9f3c9c8a3..68358a351 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ViewUserProfile.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Commands/ViewUserProfile.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Commands; +using Paralax.CQRS.Commands; namespace MiniSpace.Services.Students.Application.Commands { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs index 28ff676ea..4aa27c570 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/StudentDto.cs @@ -36,5 +36,9 @@ public class StudentDto public UserSettingsDto UserSettings { get; set; } public List GalleryOfImageUrls { get; set; } + + public bool IsOnline { get; set; } + public string DeviceType { get; set; } + public DateTime? LastActive { get; set; } } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/UserSettingsDto.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/UserSettingsDto.cs index a25e7b037..56f1eb9bc 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/UserSettingsDto.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Dto/UserSettingsDto.cs @@ -6,7 +6,7 @@ namespace MiniSpace.Services.Students.Application.Dto [ExcludeFromCodeCoverage] public class UserSettingsDto { - public Guid StudentId { get; set; } + public Guid UserId { get; set; } public string CreatedAtVisibility { get; set; } public string DateOfBirthVisibility { get; set; } public string InterestedInEventsVisibility { get; set; } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/EmailVerified.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/EmailVerified.cs index 93db65351..85890f04c 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/EmailVerified.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/EmailVerified.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Students.Application.Events.External diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/EmailVerifiedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/EmailVerifiedHandler.cs index abc18085a..40a0dbed2 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/EmailVerifiedHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/EmailVerifiedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.Extensions.Logging; using MiniSpace.Services.Students.Core.Repositories; using System.Threading; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/MediaFileDeletedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/MediaFileDeletedHandler.cs index 4d15c6180..5a5cbd2e4 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/MediaFileDeletedHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/MediaFileDeletedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Core.Repositories; using System; using System.Linq; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedInHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedInHandler.cs new file mode 100644 index 000000000..8d358f07a --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedInHandler.cs @@ -0,0 +1,36 @@ +using Paralax.CQRS.Events; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Students.Core.Repositories; +using System.Threading.Tasks; +using System.Threading; + +namespace MiniSpace.Services.Students.Application.Events.External.Handlers +{ + public class SignedInHandler : IEventHandler + { + private readonly IStudentRepository _studentRepository; + private readonly ILogger _logger; + + public SignedInHandler(IStudentRepository studentRepository, ILogger logger) + { + _studentRepository = studentRepository; + _logger = logger; + } + + public async Task HandleAsync(SignedIn @event, CancellationToken cancellationToken = default) + { + var student = await _studentRepository.GetAsync(@event.UserId); + if (student is null) + { + _logger.LogWarning($"Student with ID '{@event.UserId}' not found."); + return; + } + + student.SetOnlineStatus(true, @event.DeviceType); + student.UpdateLastActive(); + await _studentRepository.UpdateAsync(student); + + _logger.LogInformation($"Student '{@event.UserId}' is now online. Device: {@event.DeviceType}, IP: {@event.IpAddress}"); + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedOutHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedOutHandler.cs new file mode 100644 index 000000000..97dbb3a94 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedOutHandler.cs @@ -0,0 +1,35 @@ +using Paralax.CQRS.Events; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Students.Core.Repositories; +using System.Threading.Tasks; +using System.Threading; + +namespace MiniSpace.Services.Students.Application.Events.External.Handlers +{ + public class SignedOutHandler : IEventHandler + { + private readonly IStudentRepository _studentRepository; + private readonly ILogger _logger; + + public SignedOutHandler(IStudentRepository studentRepository, ILogger logger) + { + _studentRepository = studentRepository; + _logger = logger; + } + + public async Task HandleAsync(SignedOut @event, CancellationToken cancellationToken = default) + { + var student = await _studentRepository.GetAsync(@event.UserId); + if (student is null) + { + _logger.LogWarning($"Student with ID '{@event.UserId}' not found."); + return; + } + + student.SetOnlineStatus(false, null); + await _studentRepository.UpdateAsync(student); + + _logger.LogInformation($"Student '{@event.UserId}' is now offline. Device: {@event.DeviceType}"); + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedUpHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedUpHandler.cs index 3577c8026..050362e57 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedUpHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/SignedUpHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.Extensions.Logging; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; @@ -67,7 +67,10 @@ public async Task HandleAsync(SignedUp @event, CancellationToken cancellationTok string.Empty, // ContactEmail string.Empty, // PhoneNumber, string.Empty, // Country - string.Empty // City + string.Empty, // City + false, // IsOnline (default to offline) + string.Empty, // DeviceType (default) + null // LastActive (default to null) ); await _studentRepository.AddAsync(newStudent); diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledInterestInEventHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledInterestInEventHandler.cs index 856455f39..8e4927c3c 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledInterestInEventHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledInterestInEventHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledSignUpToEventHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledSignUpToEventHandler.cs index af207434c..0dbbaeb8d 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledSignUpToEventHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentCancelledSignUpToEventHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentEmailVerifiedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentEmailVerifiedHandler.cs index 3c3a4b801..867096420 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentEmailVerifiedHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentEmailVerifiedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.Extensions.Logging; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Core.Entities; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentImageUploadedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentImageUploadedHandler.cs index 0082e5442..b775a0679 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentImageUploadedHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentImageUploadedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentShowedInterestInEventHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentShowedInterestInEventHandler.cs index 318d92bda..57711d57f 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentShowedInterestInEventHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentShowedInterestInEventHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentSignedUpToEventHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentSignedUpToEventHandler.cs index 213e6f374..5c75f3100 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentSignedUpToEventHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/StudentSignedUpToEventHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TokenRefreshedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TokenRefreshedHandler.cs new file mode 100644 index 000000000..5abfc06ca --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TokenRefreshedHandler.cs @@ -0,0 +1,36 @@ +using Paralax.CQRS.Events; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Students.Core.Repositories; +using System.Threading.Tasks; +using System.Threading; + +namespace MiniSpace.Services.Students.Application.Events.External.Handlers +{ + public class TokenRefreshedHandler : IEventHandler + { + private readonly IStudentRepository _studentRepository; + private readonly ILogger _logger; + + public TokenRefreshedHandler(IStudentRepository studentRepository, ILogger logger) + { + _studentRepository = studentRepository; + _logger = logger; + } + + public async Task HandleAsync(TokenRefreshed @event, CancellationToken cancellationToken = default) + { + var student = await _studentRepository.GetAsync(@event.UserId); + if (student is null) + { + _logger.LogWarning($"Student with ID '{@event.UserId}' not found."); + return; + } + + student.SetOnlineStatus(true, @event.DeviceType); + student.UpdateLastActive(); + await _studentRepository.UpdateAsync(student); + + _logger.LogInformation($"Student '{@event.UserId}' refreshed token. Device: {@event.DeviceType}, IP: {@event.IpAddress}"); + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationDisabledHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationDisabledHandler.cs index 0e9d1a1e5..b47217cc2 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationDisabledHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationDisabledHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.Extensions.Logging; using MiniSpace.Services.Students.Core.Repositories; using System.Threading; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationEnabledHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationEnabledHandler.cs index 83ca70999..7283ddbc0 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationEnabledHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/TwoFactorAuthenticationEnabledHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using Microsoft.Extensions.Logging; using MiniSpace.Services.Students.Core.Repositories; using System.Threading; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserBannedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserBannedHandler.cs index 3c2e3c3ce..6b725e719 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserBannedHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserBannedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserStatusChangedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserStatusChangedHandler.cs new file mode 100644 index 000000000..7fccb8cc5 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserStatusChangedHandler.cs @@ -0,0 +1,36 @@ +using Paralax.CQRS.Events; +using Microsoft.Extensions.Logging; +using MiniSpace.Services.Students.Core.Repositories; +using System.Threading.Tasks; +using System.Threading; + +namespace MiniSpace.Services.Students.Application.Events.External.Handlers +{ + public class UserStatusChangedHandler : IEventHandler + { + private readonly IStudentRepository _studentRepository; + private readonly ILogger _logger; + + public UserStatusChangedHandler(IStudentRepository studentRepository, ILogger logger) + { + _studentRepository = studentRepository; + _logger = logger; + } + + public async Task HandleAsync(UserStatusChanged @event, CancellationToken cancellationToken = default) + { + var student = await _studentRepository.GetAsync(@event.UserId); + if (student is null) + { + _logger.LogWarning($"Student with ID '{@event.UserId}' not found."); + return; + } + + student.SetOnlineStatus(@event.IsOnline, @event.DeviceType); + student.UpdateLastActive(); + await _studentRepository.UpdateAsync(student); + + _logger.LogInformation($"Student '{@event.UserId}' status changed. Online: {@event.IsOnline}, Device: {@event.DeviceType}, IP: {@event.IpAddress}"); + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserUnbannedHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserUnbannedHandler.cs index b3b9f5f0b..b439a4841 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserUnbannedHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/Handlers/UserUnbannedHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/MediaFileDeleted.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/MediaFileDeleted.cs index 7de97b572..8dbc102cd 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/MediaFileDeleted.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/MediaFileDeleted.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Students.Application.Events.External diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedIn.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedIn.cs new file mode 100644 index 000000000..24399ed21 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedIn.cs @@ -0,0 +1,23 @@ +using System; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; + +namespace MiniSpace.Services.Students.Application.Events.External +{ + [Message("identity")] + public class SignedIn : IEvent + { + public Guid UserId { get; } + public string Role { get; } + public string DeviceType { get; } + public string IpAddress { get; } + + public SignedIn(Guid userId, string role, string deviceType, string ipAddress) + { + UserId = userId; + Role = role; + DeviceType = deviceType; + IpAddress = ipAddress; + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedOut.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedOut.cs new file mode 100644 index 000000000..56652a201 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedOut.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; + +namespace MiniSpace.Services.Students.Application.Events.External +{ + [Message("identity")] + public class SignedOut : IEvent + { + public Guid UserId { get; } + public string DeviceType { get; } + + public SignedOut(Guid userId, string deviceType) + { + UserId = userId; + DeviceType = deviceType; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedUp.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedUp.cs index 8b2d057bc..1b57b6023 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedUp.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/SignedUp.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledInterestInEvent.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledInterestInEvent.cs index a98902d30..282bcfa4b 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledInterestInEvent.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledInterestInEvent.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledSignUpToEvent.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledSignUpToEvent.cs index a473e59ac..0f91342a1 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledSignUpToEvent.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentCancelledSignUpToEvent.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentImageUploaded.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentImageUploaded.cs index 1e2940553..2999ed661 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentImageUploaded.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentImageUploaded.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; using System; namespace MiniSpace.Services.Students.Application.Events.External diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentShowedInterestInEvent.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentShowedInterestInEvent.cs index c42e2543c..368232424 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentShowedInterestInEvent.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentShowedInterestInEvent.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentSignedUpToEvent.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentSignedUpToEvent.cs index 39d4f18db..0e1208e7b 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentSignedUpToEvent.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/StudentSignedUpToEvent.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TokenRefreshed.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TokenRefreshed.cs new file mode 100644 index 000000000..a2796f3a0 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TokenRefreshed.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; + +namespace MiniSpace.Services.Students.Application.Events.External +{ + [Message("identity")] + public class TokenRefreshed : IEvent + { + public Guid UserId { get; } + public string DeviceType { get; } + public string IpAddress { get; } + + public TokenRefreshed(Guid userId, string deviceType, string ipAddress) + { + UserId = userId; + DeviceType = deviceType; + IpAddress = ipAddress; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationDisabled.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationDisabled.cs index c15c01df1..7ccd8b92e 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationDisabled.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationDisabled.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationEnabled.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationEnabled.cs index 02527b3fb..59b2d0813 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationEnabled.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/TwoFactorAuthenticationEnabled.cs @@ -1,6 +1,6 @@ using System; -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserBanned.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserBanned.cs index c090e204f..e957383e3 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserBanned.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserBanned.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserStatusChanged.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserStatusChanged.cs new file mode 100644 index 000000000..9b160920a --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserStatusChanged.cs @@ -0,0 +1,23 @@ +using System; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; + +namespace MiniSpace.Services.Students.Application.Events.External +{ + [Message("identity")] + public class UserStatusChanged : IEvent + { + public Guid UserId { get; } + public bool IsOnline { get; } + public string DeviceType { get; } + public string IpAddress { get; } + + public UserStatusChanged(Guid userId, bool isOnline, string deviceType, string ipAddress) + { + UserId = userId; + IsOnline = isOnline; + DeviceType = deviceType; + IpAddress = ipAddress; + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserUnbanned.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserUnbanned.cs index 967db645c..70eea7044 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserUnbanned.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/External/UserUnbanned.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; namespace MiniSpace.Services.Students.Application.Events.External { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/ChangeStudentStateRejected.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/ChangeStudentStateRejected.cs index 445e38697..535b695ce 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/ChangeStudentStateRejected.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/ChangeStudentStateRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Events.Rejected diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CompleteStudentRegistrationRejected.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CompleteStudentRegistrationRejected.cs index 9e716d324..869af31a0 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CompleteStudentRegistrationRejected.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CompleteStudentRegistrationRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Commands; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CreateStudentRejected.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CreateStudentRejected.cs index e0a617a08..6bb7629d3 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CreateStudentRejected.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/CreateStudentRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Events.Rejected diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/DeleteStudentRejected.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/DeleteStudentRejected.cs index ed3f528e1..3767ef1bc 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/DeleteStudentRejected.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/DeleteStudentRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Events.Rejected diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/UpdateStudentRejected.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/UpdateStudentRejected.cs index 18388aecb..151ca6a55 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/UpdateStudentRejected.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/Rejected/UpdateStudentRejected.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Events.Rejected diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentCreated.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentCreated.cs index b7556f615..1e18a8c3d 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentCreated.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentCreated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Events diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentDeleted.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentDeleted.cs index 853301961..c7a6e8c41 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentDeleted.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentDeleted.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Events diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentStateChanged.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentStateChanged.cs index bf2f481cd..4894e9ec0 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentStateChanged.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentStateChanged.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Events diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentUpdated.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentUpdated.cs index 14674d8d4..20e062171 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentUpdated.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/StudentUpdated.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Dto; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/UserBlocked.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/UserBlocked.cs index 120e49050..a197a8bd6 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/UserBlocked.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Events/UserBlocked.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using System; namespace MiniSpace.Services.Students.Application.Events diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Extensions.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Extensions.cs index 85b4cb42e..7be8ffc93 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Extensions.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Extensions.cs @@ -1,6 +1,6 @@ -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application @@ -8,7 +8,7 @@ namespace MiniSpace.Services.Students.Application [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddApplication(this IConveyBuilder builder) + public static IParalaxBuilder AddApplication(this IParalaxBuilder builder) => builder .AddCommandHandlers() .AddEventHandlers() diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/MiniSpace.Services.Students.Application.csproj b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/MiniSpace.Services.Students.Application.csproj index fdf5b09ed..b0c854519 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/MiniSpace.Services.Students.Application.csproj +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/MiniSpace.Services.Students.Application.csproj @@ -7,11 +7,11 @@ - - - - - + + + + + diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetBlockedUsers.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetBlockedUsers.cs index 95ef7c7d9..6fdc56ae4 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetBlockedUsers.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetBlockedUsers.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Core.Wrappers; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetProfilesViewedByUser.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetProfilesViewedByUser.cs index 9602600d4..c7025ebff 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetProfilesViewedByUser.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetProfilesViewedByUser.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Core.Wrappers; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudent.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudent.cs index bcbce3876..ad97b326d 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudent.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudent.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentEvents.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentEvents.cs index 0ef959fe0..c75cd4f32 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentEvents.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentEvents.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentImages.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentImages.cs index 8c7aad8d4..2f8aa6d3a 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentImages.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentImages.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithGalleryImages.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithGalleryImages.cs index 0b3a221e3..253948c09 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithGalleryImages.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithGalleryImages.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithVisibilitySettings.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithVisibilitySettings.cs index 4ce9c23ec..7ee997b58 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithVisibilitySettings.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudentWithVisibilitySettings.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudents.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudents.cs index ca4aa8312..a8b2a0918 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudents.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetStudents.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserNotificationPreferences.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserNotificationPreferences.cs index b2b58cd51..356ecb48c 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserNotificationPreferences.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserNotificationPreferences.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserProfileViews.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserProfileViews.cs index dd8344685..d097cf5a2 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserProfileViews.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserProfileViews.cs @@ -1,5 +1,5 @@ using System; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Core.Wrappers; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserSettings.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserSettings.cs index 0edc10209..3248bd7c1 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserSettings.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/GetUserSettings.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using System; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/IPagedGetStudentsQuery.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/IPagedGetStudentsQuery.cs index d37adcf2e..f9c2a592b 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/IPagedGetStudentsQuery.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/IPagedGetStudentsQuery.cs @@ -1,8 +1,8 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; namespace MiniSpace.Services.Students.Application.Queries { - public interface IPagedGetStudentsQuery : Convey.CQRS.Queries.IPagedQuery + public interface IPagedGetStudentsQuery : Paralax.CQRS.Queries.IPagedQuery { new int Page { get; set; } new int ResultsPerPage { get; set; } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/PagedResult.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/PagedResult.cs index 17904904f..ab5542916 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/PagedResult.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Queries/PagedResult.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Application.Queries diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IEventMapper.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IEventMapper.cs index b945e8700..98d2b91eb 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IEventMapper.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IEventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Core.Events; namespace MiniSpace.Services.Students.Application.Services diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IMessageBroker.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IMessageBroker.cs index 857148c25..a6e04818f 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IMessageBroker.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Application/Services/IMessageBroker.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; namespace MiniSpace.Services.Students.Application.Services { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/Student.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/Student.cs index 5a6cfde4c..0f6b0c35a 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/Student.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/Student.cs @@ -30,6 +30,10 @@ public class Student : AggregateRoot public string PhoneNumber { get; private set; } public string Country { get; private set; } public string City { get; private set; } + + public bool IsOnline { get; private set; } + public string DeviceType { get; private set; } + public DateTime? LastActive { get; private set; } public IEnumerable Languages { get => _languages; @@ -70,7 +74,8 @@ public Student(Guid id, string email, DateTime createdAt, string firstName, stri string bannerUrl, IEnumerable education, IEnumerable work, IEnumerable languages, IEnumerable interests, bool isTwoFactorEnabled, string twoFactorSecret, string contactEmail, - string phoneNumber, string country, string city) + string phoneNumber, string country, string city, + bool isOnline = false, string deviceType = null, DateTime? lastActive = null) { Id = id; Email = email; @@ -96,6 +101,9 @@ public Student(Guid id, string email, DateTime createdAt, string firstName, stri PhoneNumber = phoneNumber; Country = country; City = city; + IsOnline = isOnline; + DeviceType = deviceType; + LastActive = lastActive; } public void SetIncomplete() => SetState(State.Incomplete); @@ -309,5 +317,20 @@ public void SetEmailNotifications(bool emailNotifications) EmailNotifications = emailNotifications; AddEvent(new StudentUpdated(this)); } + + public void SetOnlineStatus(bool isOnline, string deviceType) + { + IsOnline = isOnline; + DeviceType = isOnline ? deviceType : null; + LastActive = DateTime.UtcNow; + + AddEvent(new StudentOnlineStatusChanged(this)); + } + + public void UpdateLastActive() + { + LastActive = DateTime.UtcNow; + AddEvent(new StudentLastActiveUpdated(this)); + } } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserNotifications.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserNotifications.cs index 26ae175aa..5c5dd8c5e 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserNotifications.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserNotifications.cs @@ -5,12 +5,12 @@ namespace MiniSpace.Services.Students.Core.Entities { public class UserNotifications : AggregateRoot { - public Guid StudentId { get; private set; } + public Guid UserId { get; private set; } public NotificationPreferences NotificationPreferences { get; private set; } - public UserNotifications(Guid studentId, NotificationPreferences notificationPreferences) + public UserNotifications(Guid userId, NotificationPreferences notificationPreferences) { - StudentId = studentId; + UserId = userId; NotificationPreferences = notificationPreferences ?? new NotificationPreferences(); } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserSettings.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserSettings.cs index d5687725e..0d70e26e8 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserSettings.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Entities/UserSettings.cs @@ -5,12 +5,12 @@ namespace MiniSpace.Services.Students.Core.Entities { public class UserSettings : AggregateRoot { - public Guid StudentId { get; private set; } + public Guid UserId { get; private set; } public UserAvailableSettings AvailableSettings { get; private set; } - public UserSettings(Guid studentId, UserAvailableSettings availableSettings) + public UserSettings(Guid userId, UserAvailableSettings availableSettings) { - StudentId = studentId; + UserId = userId; AvailableSettings = availableSettings ?? new UserAvailableSettings(); } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentLastActiveUpdated.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentLastActiveUpdated.cs new file mode 100644 index 000000000..ece3bf0e1 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentLastActiveUpdated.cs @@ -0,0 +1,17 @@ +using System; +using MiniSpace.Services.Students.Core.Entities; + +namespace MiniSpace.Services.Students.Core.Events +{ + public class StudentLastActiveUpdated : IDomainEvent + { + public Guid UserId { get; } + public DateTime LastActive { get; } + + public StudentLastActiveUpdated(Student student) + { + UserId = student.Id; + LastActive = student.LastActive ?? DateTime.UtcNow; + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentNotificationPreferencesUpdated.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentNotificationPreferencesUpdated.cs index d3059bfed..6a0ffd94b 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentNotificationPreferencesUpdated.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentNotificationPreferencesUpdated.cs @@ -5,12 +5,12 @@ namespace MiniSpace.Services.Students.Core.Events { public class StudentNotificationPreferencesUpdated : IDomainEvent { - public Guid StudentId { get; } + public Guid UserId { get; } public NotificationPreferences NotificationPreferences { get; } public StudentNotificationPreferencesUpdated(UserNotifications userNotifications) { - StudentId = userNotifications.StudentId; + UserId = userNotifications.UserId; NotificationPreferences = userNotifications.NotificationPreferences; } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentOnlineStatusChanged.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentOnlineStatusChanged.cs new file mode 100644 index 000000000..b6b92aaf5 --- /dev/null +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Events/StudentOnlineStatusChanged.cs @@ -0,0 +1,21 @@ +using System; +using MiniSpace.Services.Students.Core.Entities; + +namespace MiniSpace.Services.Students.Core.Events +{ + public class StudentOnlineStatusChanged : IDomainEvent + { + public Guid StudentId { get; } + public bool IsOnline { get; } + public string DeviceType { get; } + public DateTime? LastActive { get; } + + public StudentOnlineStatusChanged(Student student) + { + StudentId = student.Id; + IsOnline = student.IsOnline; + DeviceType = student.DeviceType; + LastActive = student.LastActive; + } + } +} diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadyInterestedInException.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadyInterestedInException.cs index d45156d48..ac41f8914 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadyInterestedInException.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadyInterestedInException.cs @@ -3,13 +3,13 @@ public class StudentAlreadyInterestedInException : DomainException { public override string Code { get; } = "student_already_interested_in_event"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentAlreadyInterestedInException(Guid studentId, Guid eventId) - : base($"Student with id: {studentId} is already interested in event with id: {eventId}") + public StudentAlreadyInterestedInException(Guid userId, Guid eventId) + : base($"Student with id: {userId} is already interested in event with id: {eventId}") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadySignedUpException.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadySignedUpException.cs index 156aea27b..f59a9700b 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadySignedUpException.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentAlreadySignedUpException.cs @@ -3,13 +3,13 @@ public class StudentAlreadySignedUpException : DomainException { public override string Code { get; } = "student_already_signed_up"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentAlreadySignedUpException(Guid studentId, Guid eventId) - : base($"Student with id: {studentId} is already signed up for event with id: {eventId}") + public StudentAlreadySignedUpException(Guid userId, Guid eventId) + : base($"Student with id: {userId} is already signed up for event with id: {eventId}") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentGalleryImageNotFoundException.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentGalleryImageNotFoundException.cs index d662bd817..401b49602 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentGalleryImageNotFoundException.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentGalleryImageNotFoundException.cs @@ -3,13 +3,13 @@ namespace MiniSpace.Services.Students.Core.Exceptions public class StudentGalleryImageNotFoundException : DomainException { public override string Code { get; } = "student_gallery_image_not_found"; - public Guid StudentId { get; } + public Guid UserId { get; } public string MediaFileId { get; } - public StudentGalleryImageNotFoundException(Guid studentId, string mediaFileId) - : base($"Student with id: {studentId} does not have an image with media file id: {mediaFileId} in the gallery.") + public StudentGalleryImageNotFoundException(Guid userId, string mediaFileId) + : base($"Student with id: {userId} does not have an image with media file id: {mediaFileId} in the gallery.") { - StudentId = studentId; + UserId = userId; MediaFileId = mediaFileId; } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotInterestedException.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotInterestedException.cs index 6f50d8058..f9813f6ec 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotInterestedException.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotInterestedException.cs @@ -3,13 +3,13 @@ public class StudentIsNotInterestedException : DomainException { public override string Code { get; } = "student_is_not_interested_in_event"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentIsNotInterestedException(Guid studentId, Guid eventId) - : base($"Student with id: {studentId} is not interested in event with id: {eventId}") + public StudentIsNotInterestedException(Guid userId, Guid eventId) + : base($"Student with id: {userId} is not interested in event with id: {eventId}") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotSignedUpException.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotSignedUpException.cs index d4ee84d75..295306069 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotSignedUpException.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Exceptions/StudentIsNotSignedUpException.cs @@ -3,13 +3,13 @@ public class StudentIsNotSignedUpException : DomainException { public override string Code { get; } = "student_is_not_signed_up_to_event"; - public Guid StudentId { get; } + public Guid UserId { get; } public Guid EventId { get; } - public StudentIsNotSignedUpException(Guid studentId, Guid eventId) - : base($"Student with id: {studentId} is not signed up to event with id: {eventId}") + public StudentIsNotSignedUpException(Guid userId, Guid eventId) + : base($"Student with id: {userId} is not signed up to event with id: {eventId}") { - StudentId = studentId; + UserId = userId; EventId = eventId; } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserNotificationPreferencesRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserNotificationPreferencesRepository.cs index 5783c7604..5f49054b5 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserNotificationPreferencesRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserNotificationPreferencesRepository.cs @@ -6,7 +6,7 @@ namespace MiniSpace.Services.Students.Core.Repositories { public interface IUserNotificationPreferencesRepository { - Task GetNotificationPreferencesAsync(Guid studentId); - Task UpdateNotificationPreferencesAsync(Guid studentId, NotificationPreferences notificationPreferences); + Task GetNotificationPreferencesAsync(Guid userId); + Task UpdateNotificationPreferencesAsync(Guid userId, NotificationPreferences notificationPreferences); } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserSettingsRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserSettingsRepository.cs index aaa7b1498..9f06887fc 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserSettingsRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Core/Repositories/IUserSettingsRepository.cs @@ -6,7 +6,7 @@ namespace MiniSpace.Services.Students.Core.Repositories { public interface IUserSettingsRepository { - Task GetUserSettingsAsync(Guid studentId); + Task GetUserSettingsAsync(Guid userId); Task AddUserSettingsAsync(UserSettings userSettings); Task UpdateUserSettingsAsync(UserSettings userSettings); } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Contexts/AppContextFactory.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Contexts/AppContextFactory.cs index bae4bd562..e5bb574e6 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Contexts/AppContextFactory.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Contexts/AppContextFactory.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers; +using Paralax.MessageBrokers; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; using MiniSpace.Services.Students.Application; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs index 16977ec21..42cd70a1b 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxCommandHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Commands; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Commands; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Infrastructure.Decorators diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs index 2b9c9b5c6..399ac73c6 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Decorators/OutboxEventHandlerDecorator.cs @@ -1,7 +1,8 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.Types; +using Paralax.Core; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.Types; using System.Diagnostics.CodeAnalysis; namespace MiniSpace.Services.Students.Infrastructure.Decorators diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToMessageMapper.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToMessageMapper.cs index 6ee6950a8..b0c61b9f8 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToMessageMapper.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToMessageMapper.cs @@ -1,4 +1,4 @@ -using Convey.MessageBrokers.RabbitMQ; +using Paralax.MessageBrokers.RabbitMQ; using MiniSpace.Services.Students.Application.Commands; using MiniSpace.Services.Students.Application.Events.Rejected; using MiniSpace.Services.Students.Application.Events.External; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToResponseMapper.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToResponseMapper.cs index 5e6bcd3c0..0f027e7d5 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToResponseMapper.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Exceptions/ExceptionToResponseMapper.cs @@ -1,8 +1,8 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Net; -using Convey; -using Convey.WebApi.Exceptions; +using Paralax; +using Paralax.WebApi.Exceptions; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Core.Exceptions; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Extensions.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Extensions.cs index fb3b66923..9945bd13c 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Extensions.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Extensions.cs @@ -1,27 +1,27 @@ using System.Text; -using Convey; -using Convey.CQRS.Commands; -using Convey.CQRS.Events; -using Convey.CQRS.Queries; -using Convey.Discovery.Consul; -using Convey.Docs.Swagger; -using Convey.HTTP; -using Convey.LoadBalancing.Fabio; -using Convey.MessageBrokers; -using Convey.MessageBrokers.CQRS; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.Outbox.Mongo; -using Convey.MessageBrokers.RabbitMQ; -using Convey.Metrics.AppMetrics; -using Convey.Persistence.MongoDB; -using Convey.Persistence.Redis; -using Convey.Security; -using Convey.Tracing.Jaeger; -using Convey.Tracing.Jaeger.RabbitMQ; -using Convey.WebApi; -using Convey.WebApi.CQRS; -using Convey.WebApi.Security; -using Convey.WebApi.Swagger; +using Paralax; +using Paralax.CQRS.Commands; +using Paralax.CQRS.Events; +using Paralax.CQRS.Queries; +using Paralax.Discovery.Consul; +using Paralax.Docs.Swagger; +using Paralax.HTTP; +using Paralax.LoadBalancing.Fabio; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.CQRS; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.Outbox.Mongo; +using Paralax.MessageBrokers.RabbitMQ; +using Paralax.Metrics.AppMetrics; +using Paralax.Persistence.MongoDB; +using Paralax.Persistence.Redis; +using Paralax.Security; +using Paralax.Tracing.Jaeger; +using Paralax.Tracing.Jaeger.RabbitMQ; +using Paralax.WebApi; +using Paralax.CQRS.WebApi; +using Paralax.WebApi.Security; +using Paralax.WebApi.Swagger; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; @@ -41,14 +41,15 @@ using MiniSpace.Services.Students.Infrastructure.Services; using MongoDB.Driver; using System.Diagnostics.CodeAnalysis; -using Convey.Types; +using Paralax.Types; +using Paralax.CQRS.WebApi; namespace MiniSpace.Services.Students.Infrastructure { [ExcludeFromCodeCoverage] public static class Extensions { - public static IConveyBuilder AddInfrastructure(this IConveyBuilder builder) + public static IParalaxBuilder AddInfrastructure(this IParalaxBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); @@ -99,7 +100,7 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app app.UseErrorHandler() .UseSwaggerDocs() .UseJaeger() - .UseConvey() + .UseParalax() .UsePublicContracts() .UseMetrics() .UseCertificateAuthentication() @@ -112,6 +113,10 @@ public static IApplicationBuilder UseInfrastructure(this IApplicationBuilder app .SubscribeCommand() .SubscribeEvent() .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent() + .SubscribeEvent() .SubscribeEvent() .SubscribeEvent() .SubscribeEvent() diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/Extensions.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/Extensions.cs index 5927f1f17..678dc0bbb 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/Extensions.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/Extensions.cs @@ -1,15 +1,15 @@ -using Convey; -using Convey.Logging.CQRS; +using Paralax; using Microsoft.Extensions.DependencyInjection; using MiniSpace.Services.Students.Application.Commands; using System.Diagnostics.CodeAnalysis; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Students.Infrastructure.Logging { [ExcludeFromCodeCoverage] internal static class Extensions { - public static IConveyBuilder AddHandlersLogging(this IConveyBuilder builder) + public static IParalaxBuilder AddHandlersLogging(this IParalaxBuilder builder) { var assembly = typeof(UpdateStudent).Assembly; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/MessageToLogTemplateMapper.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/MessageToLogTemplateMapper.cs index 66b3e1374..ff72c2ca8 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/MessageToLogTemplateMapper.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Logging/MessageToLogTemplateMapper.cs @@ -1,7 +1,7 @@ -using Convey.Logging.CQRS; using MiniSpace.Services.Students.Application.Commands; using MiniSpace.Services.Students.Application.Events.External; using System.Diagnostics.CodeAnalysis; +using Paralax.CQRS.Logging; namespace MiniSpace.Services.Students.Infrastructure.Logging { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/MiniSpace.Services.Students.Infrastructure.csproj b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/MiniSpace.Services.Students.Infrastructure.csproj index 154f2f7f1..e55e5404f 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/MiniSpace.Services.Students.Infrastructure.csproj +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/MiniSpace.Services.Students.Infrastructure.csproj @@ -7,33 +7,44 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/BlockedUsersDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/BlockedUsersDocument.cs index a1b568ede..3f81cfaba 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/BlockedUsersDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/BlockedUsersDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/EducationDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/EducationDocument.cs index a30fc4587..496ac003d 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/EducationDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/EducationDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/Extensions.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/Extensions.cs index 3e5473469..7426bec5c 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/Extensions.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/Extensions.cs @@ -33,7 +33,10 @@ public static Student AsEntity(this StudentDocument document) document.ContactEmail, document.PhoneNumber, document.Country, - document.City + document.City, + document.IsOnline, + document.DeviceType, + document.LastActive ); public static StudentDocument AsDocument(this Student entity) @@ -77,6 +80,9 @@ public static StudentDocument AsDocument(this Student entity) PhoneNumber = entity.PhoneNumber, Country = entity.Country, City = entity.City, + IsOnline = entity.IsOnline, + DeviceType = entity.DeviceType, + LastActive = entity.LastActive }; public static StudentDto AsDto(this StudentDocument document) @@ -120,11 +126,14 @@ public static StudentDto AsDto(this StudentDocument document) PhoneNumber = document.PhoneNumber, Country = document.Country, City = document.City, + IsOnline = document.IsOnline, + DeviceType = document.DeviceType, + LastActive = document.LastActive }; public static UserNotifications AsEntity(this UserNotificationsDocument document) => new UserNotifications( - document.StudentId, + document.UserId, document.NotificationPreferences ); @@ -132,7 +141,7 @@ public static UserNotificationsDocument AsDocument(this UserNotifications entity => new UserNotificationsDocument { Id = Guid.NewGuid(), // Ensure a unique identifier is set - StudentId = entity.StudentId, + UserId = entity.UserId, NotificationPreferences = entity.NotificationPreferences }; @@ -153,7 +162,7 @@ public static UserNotificationsDocument AsDocument(this NotificationPreferencesD => new UserNotificationsDocument { Id = Guid.NewGuid(), - StudentId = dto.StudentId, + UserId = dto.StudentId, NotificationPreferences = new NotificationPreferences( dto.AccountChanges, dto.SystemLogin, @@ -189,7 +198,7 @@ public static UserGalleryDto AsDto(this UserGalleryDocument document) public static UserSettings AsEntity(this UserSettingsDocument document) => new UserSettings( - document.StudentId, + document.UserId, new UserAvailableSettings( document.AvailableSettings.CreatedAtVisibility, document.AvailableSettings.DateOfBirthVisibility, @@ -212,8 +221,8 @@ public static UserSettings AsEntity(this UserSettingsDocument document) public static UserSettingsDocument AsDocument(this UserSettings entity) => new UserSettingsDocument { - Id = Guid.NewGuid(), // Ensure a unique identifier is set - StudentId = entity.StudentId, + Id = Guid.NewGuid(), + UserId = entity.UserId, AvailableSettings = new UserAvailableSettingsDocument { CreatedAtVisibility = entity.AvailableSettings.CreatedAtVisibility, diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/GalleryImageDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/GalleryImageDocument.cs index c9d8cef73..4e5c54260 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/GalleryImageDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/GalleryImageDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/StudentDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/StudentDocument.cs index 2c0b5e6c7..1a93bf7be 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/StudentDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/StudentDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; using System; using System.Collections.Generic; @@ -33,5 +33,9 @@ public class StudentDocument : IIdentifiable public string PhoneNumber { get; set; } public string Country { get; set; } public string City { get; set; } + + public bool IsOnline { get; set; } + public string DeviceType { get; set; } + public DateTime? LastActive { get; set; } } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserGalleryDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserGalleryDocument.cs index 729d776a4..3345a4e02 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserGalleryDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserGalleryDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserNotificationsDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserNotificationsDocument.cs index 30a08c849..68e4753d7 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserNotificationsDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserNotificationsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; using System; using System.Diagnostics.CodeAnalysis; @@ -9,7 +9,7 @@ namespace MiniSpace.Services.Students.Infrastructure.Mongo.Documents public class UserNotificationsDocument : IIdentifiable { public Guid Id { get; set; } - public Guid StudentId { get; set; } + public Guid UserId { get; set; } public NotificationPreferences NotificationPreferences { get; set; } } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewDocument.cs index 4ef955c15..8fd337d77 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewDocument.cs @@ -1,5 +1,5 @@ using System; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; namespace MiniSpace.Services.Students.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewsDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewsDocument.cs index 8050d1571..94a939dad 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewsDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserProfileViewsDocument.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; namespace MiniSpace.Services.Students.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserSettingsDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserSettingsDocument.cs index 9f0b941cf..50e1149a7 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserSettingsDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserSettingsDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; using System; using System.Diagnostics.CodeAnalysis; @@ -9,7 +9,7 @@ namespace MiniSpace.Services.Students.Infrastructure.Mongo.Documents public class UserSettingsDocument : IIdentifiable { public Guid Id { get; set; } - public Guid StudentId { get; set; } + public Guid UserId { get; set; } public UserAvailableSettingsDocument AvailableSettings { get; set; } } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserViewingProfilesDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserViewingProfilesDocument.cs index fde8d2e2c..aadf5590d 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserViewingProfilesDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/UserViewingProfilesDocument.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; namespace MiniSpace.Services.Students.Infrastructure.Mongo.Documents diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/WorkDocument.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/WorkDocument.cs index 488707092..6fc47c75b 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/WorkDocument.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Documents/WorkDocument.cs @@ -1,4 +1,4 @@ -using Convey.Types; +using Paralax.Types; using MiniSpace.Services.Students.Core.Entities; using System; using System.Collections.Generic; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetBlockedUsersHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetBlockedUsersHandler.cs index e02920025..1b18a08e1 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetBlockedUsersHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetBlockedUsersHandler.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetProfilesViewedByUserHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetProfilesViewedByUserHandler.cs index 12b537591..6082f6271 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetProfilesViewedByUserHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetProfilesViewedByUserHandler.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Core.Repositories; using MiniSpace.Services.Students.Core.Wrappers; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs index b786de9eb..671819e73 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentEventsHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Exceptions; using MiniSpace.Services.Students.Application.Queries; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs index 85137644e..e08b4b332 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Core.Repositories; @@ -46,7 +46,7 @@ public async Task HandleAsync(GetStudent query, CancellationToken ca // Map user settings to UserSettingsDto and include them in the StudentDto studentDto.UserSettings = new UserSettingsDto { - StudentId = userSettings.StudentId, + UserId = userSettings.UserId, CreatedAtVisibility = userSettings.AvailableSettings.CreatedAtVisibility.ToString(), DateOfBirthVisibility = userSettings.AvailableSettings.DateOfBirthVisibility.ToString(), InterestedInEventsVisibility = userSettings.AvailableSettings.InterestedInEventsVisibility.ToString(), diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithGalleryImagesHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithGalleryImagesHandler.cs index 84beb0d0f..9743da461 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithGalleryImagesHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithGalleryImagesHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithVisibilitySettingsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithVisibilitySettingsHandler.cs index d4fdb3fc4..580060c42 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithVisibilitySettingsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentWithVisibilitySettingsHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; @@ -28,7 +28,7 @@ public async Task HandleAsync(GetStudentWithVi return null; } - var settingsDocument = await _settingsRepository.GetAsync(s => s.StudentId == query.StudentId); + var settingsDocument = await _settingsRepository.GetAsync(s => s.UserId == query.StudentId); if (settingsDocument == null) { return null; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs index fb2b7c3b9..4c2d6ae10 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetStudentsHandler.cs @@ -1,6 +1,6 @@ using System.Text.RegularExpressions; -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; @@ -87,7 +87,7 @@ public GetStudentsHandler( { studentDto.UserSettings = new UserSettingsDto { - StudentId = userSettings.StudentId, + UserId = userSettings.UserId, CreatedAtVisibility = userSettings.AvailableSettings.CreatedAtVisibility.ToString(), DateOfBirthVisibility = userSettings.AvailableSettings.DateOfBirthVisibility.ToString(), InterestedInEventsVisibility = userSettings.AvailableSettings.InterestedInEventsVisibility.ToString(), diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserNotificationPreferencesHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserNotificationPreferencesHandler.cs index 742d4ed38..df085dd0d 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserNotificationPreferencesHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserNotificationPreferencesHandler.cs @@ -1,5 +1,5 @@ -using Convey.CQRS.Queries; -using Convey.Persistence.MongoDB; +using Paralax.CQRS.Queries; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; @@ -21,7 +21,7 @@ public GetUserNotificationPreferencesHandler(IMongoRepository HandleAsync(GetUserNotificationPreferences query, CancellationToken cancellationToken) { - var userNotificationsDocument = await _repository.GetAsync(x => x.StudentId == query.StudentId); + var userNotificationsDocument = await _repository.GetAsync(x => x.UserId == query.StudentId); return userNotificationsDocument?.NotificationPreferences.AsDto(); } } diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserProfileViewsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserProfileViewsHandler.cs index 89a28990e..78fe1c307 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserProfileViewsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserProfileViewsHandler.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserSettingsHandler.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserSettingsHandler.cs index a7359458e..ff76176e0 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserSettingsHandler.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Queries/Handlers/GetUserSettingsHandler.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Queries; +using Paralax.CQRS.Queries; using MiniSpace.Services.Students.Application.Dto; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Core.Repositories; @@ -28,7 +28,7 @@ public async Task HandleAsync(GetUserSettings query, Cancellati return new UserSettingsDto { - StudentId = userSettings.StudentId, + UserId = userSettings.UserId, CreatedAtVisibility = userSettings.AvailableSettings.CreatedAtVisibility.ToString(), DateOfBirthVisibility = userSettings.AvailableSettings.DateOfBirthVisibility.ToString(), InterestedInEventsVisibility = userSettings.AvailableSettings.InterestedInEventsVisibility.ToString(), diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/BlockedUsersMongoRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/BlockedUsersMongoRepository.cs index be16ef111..84e94692c 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/BlockedUsersMongoRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/BlockedUsersMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MongoDB.Driver; using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs index 42d1014b3..35d8e9193 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/StudentMongoRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MongoDB.Driver; using MiniSpace.Services.Students.Application.Queries; using MiniSpace.Services.Students.Core.Entities; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserGalleryRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserGalleryRepository.cs index b6cec314d..802dd63e3 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserGalleryRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserGalleryRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserNotificationPreferencesRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserNotificationPreferencesRepository.cs index 1beb99f2c..41d8b8480 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserNotificationPreferencesRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserNotificationPreferencesRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MongoDB.Driver; using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; @@ -21,20 +21,20 @@ public UserNotificationPreferencesRepository(IMongoRepository GetNotificationPreferencesAsync(Guid studentId) { - var userNotificationsDocument = await _repository.GetAsync(x => x.StudentId == studentId); + var userNotificationsDocument = await _repository.GetAsync(x => x.UserId == studentId); return userNotificationsDocument?.NotificationPreferences; } public async Task UpdateNotificationPreferencesAsync(Guid studentId, NotificationPreferences notificationPreferences) { - var userNotificationsDocument = await _repository.GetAsync(x => x.StudentId == studentId); + var userNotificationsDocument = await _repository.GetAsync(x => x.UserId == studentId); if (userNotificationsDocument == null) { userNotificationsDocument = new UserNotificationsDocument { Id = Guid.NewGuid(), - StudentId = studentId, + UserId = studentId, NotificationPreferences = notificationPreferences }; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserProfileViewsRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserProfileViewsRepository.cs index f9d92fb43..857222187 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserProfileViewsRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserProfileViewsRepository.cs @@ -3,7 +3,7 @@ using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; namespace MiniSpace.Services.Students.Infrastructure.Mongo.Repositories { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserSettingsRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserSettingsRepository.cs index cebcf69f9..18e9ef816 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserSettingsRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserSettingsRepository.cs @@ -1,4 +1,4 @@ -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; @@ -20,7 +20,7 @@ public UserSettingsRepository(IMongoRepository repos public async Task GetUserSettingsAsync(Guid studentId) { - var userSettingsDocument = await _repository.GetAsync(x => x.StudentId == studentId); + var userSettingsDocument = await _repository.GetAsync(x => x.UserId == studentId); return userSettingsDocument?.AsEntity(); } @@ -32,7 +32,7 @@ public async Task AddUserSettingsAsync(UserSettings userSettings) public async Task UpdateUserSettingsAsync(UserSettings userSettings) { - var userSettingsDocument = await _repository.GetAsync(x => x.StudentId == userSettings.StudentId); + var userSettingsDocument = await _repository.GetAsync(x => x.UserId == userSettings.UserId); if (userSettingsDocument == null) { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserViewingProfilesRepository.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserViewingProfilesRepository.cs index e24526e2a..b05c25ffe 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserViewingProfilesRepository.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Mongo/Repositories/UserViewingProfilesRepository.cs @@ -3,7 +3,7 @@ using MiniSpace.Services.Students.Core.Entities; using MiniSpace.Services.Students.Core.Repositories; using MiniSpace.Services.Students.Infrastructure.Mongo.Documents; -using Convey.Persistence.MongoDB; +using Paralax.Persistence.MongoDB; namespace MiniSpace.Services.Students.Infrastructure.Mongo.Repositories { diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/EventMapper.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/EventMapper.cs index 98f30eca2..b52f4ca56 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/EventMapper.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/EventMapper.cs @@ -1,4 +1,4 @@ -using Convey.CQRS.Events; +using Paralax.CQRS.Events; using MiniSpace.Services.Students.Application.Services; using MiniSpace.Services.Students.Core; using MiniSpace.Services.Students.Core.Events; diff --git a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/MessageBroker.cs b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/MessageBroker.cs index 2ece59c73..a3b447691 100644 --- a/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/MessageBroker.cs +++ b/MiniSpace.Services.Students/src/MiniSpace.Services.Students.Infrastructure/Services/MessageBroker.cs @@ -1,7 +1,7 @@ -using Convey.CQRS.Events; -using Convey.MessageBrokers; -using Convey.MessageBrokers.Outbox; -using Convey.MessageBrokers.RabbitMQ; +using Paralax.CQRS.Events; +using Paralax.MessageBrokers; +using Paralax.MessageBrokers.Outbox; +using Paralax.MessageBrokers.RabbitMQ; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenTracing; diff --git a/MiniSpace.Web/src/MiniSpacePwa/App.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/App.razor similarity index 100% rename from MiniSpace.Web/src/MiniSpacePwa/App.razor rename to MiniSpace.Web/src/Astravent.Web.Wasm/App.razor diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/AddLikeDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/AddLikeDto.cs new file mode 100644 index 000000000..f75393ef4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/AddLikeDto.cs @@ -0,0 +1,18 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Comments.CommandsDto +{ + public class AddLikeDto + { + public Guid CommentId { get; } + public Guid UserId { get; } + public string CommentContext { get; } + + public AddLikeDto(Guid commentId, Guid userId, string commentContext) + { + CommentId = commentId; + UserId = userId; + CommentContext = commentContext; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/CreateCommentCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/CreateCommentCommand.cs new file mode 100644 index 000000000..47d81b644 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/CreateCommentCommand.cs @@ -0,0 +1,25 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Comments.CommandsDto +{ + public class CreateCommentCommand + { + public Guid CommentId { get; set; } + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + // CommentsContext := UserPost || UserEvent || OrganizationPost || OrganizationEvent + public Guid UserId { get; set; } + public Guid ParentId { get; set; } + public string TextContent { get; set; } + + public CreateCommentCommand(Guid commentId, Guid contextId, string commentContext, Guid userId, Guid parentId, string textContent) + { + CommentId = commentId == Guid.Empty ? Guid.NewGuid() : commentId; + ContextId = contextId; + CommentContext = commentContext; + UserId = userId; + ParentId = parentId; + TextContent = textContent; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/SearchRootCommentsCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/SearchRootCommentsCommand.cs new file mode 100644 index 000000000..a49a7bc13 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/SearchRootCommentsCommand.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Areas.Comments.CommandsDto +{ + public class SearchRootCommentsCommand + { + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + public PageableDto Pageable { get; set; } + + public SearchRootCommentsCommand(Guid contextId, string commentContext, PageableDto pageable) + { + ContextId = contextId; + CommentContext = commentContext; + Pageable = pageable; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/SearchSubCommentsCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/SearchSubCommentsCommand.cs new file mode 100644 index 000000000..ceb742609 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/SearchSubCommentsCommand.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Areas.Comments.CommandsDto +{ + public class SearchSubCommentsCommand + { + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + public Guid ParentId { get; set; } + public PageableDto Pageable { get; set; } + + public SearchSubCommentsCommand(Guid contextId, string commentContext, + Guid parentId, PageableDto pageable) + { + ContextId = contextId; + CommentContext = commentContext; + ParentId = parentId; + Pageable = pageable; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/UpdateCommentCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/UpdateCommentCommand.cs new file mode 100644 index 000000000..c12126c93 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommandsDto/UpdateCommentCommand.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Comments.CommandsDto +{ + public class UpdateCommentCommand + { + public Guid CommentId { get; set; } + public string TextContent { get; set; } + + public UpdateCommentCommand(Guid commentId, string textContent) + { + CommentId = commentId; + TextContent = textContent; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommentsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommentsService.cs new file mode 100644 index 000000000..6ca36714d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/CommentsService.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web; +using Astravent.Web.Wasm.Areas.Comments.CommandsDto; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.DTO.Comments; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Comments +{ + public class CommentsService : ICommentsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public CommentsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public Task> SearchRootCommentsAsync(SearchRootCommentsCommand command) + { + var queryString = ToQueryString(command); + + // Log the query string to the console + Console.WriteLine($"Sending request with query string: comments/search{queryString}"); + + return _httpClient.GetAsync>($"comments/search{queryString}"); + } + + + private string ToQueryString(SearchRootCommentsCommand command) + { + var query = HttpUtility.ParseQueryString(string.Empty); + query["ContextId"] = command.ContextId.ToString(); + query["CommentContext"] = command.CommentContext; + + // Flatten the PageableDto into individual query parameters + if (command.Pageable != null) + { + query["Page"] = command.Pageable.Page.ToString(); + query["Size"] = command.Pageable.Size.ToString(); + + if (command.Pageable.Sort != null) + { + // Pass SortBy as a comma-separated list + if (command.Pageable.Sort.SortBy != null && command.Pageable.Sort.SortBy.Any()) + { + query["SortBy"] = string.Join(",", command.Pageable.Sort.SortBy); + } + query["Direction"] = command.Pageable.Sort.Direction; + } + } + + return "?" + query.ToString(); + } + + + public Task>> SearchSubCommentsAsync(SearchSubCommentsCommand command) + { + return _httpClient.PostAsync>("comments/search", command); + } + + public Task GetCommentAsync(Guid commentId) + { + return _httpClient.GetAsync($"comments/{commentId}"); + } + + public Task> CreateCommentAsync(CreateCommentCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync("comments", command); + } + + public Task> UpdateCommentAsync(UpdateCommentCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"comments/{command.CommentId}", command); + } + + public Task DeleteCommentAsync(Guid commentId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.DeleteAsync($"comments/{commentId}"); + } + + public Task> AddLikeAsync(AddLikeDto command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"comments/{command.CommentId}/like", command); + } + + public Task DeleteLikeAsync(Guid commentId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.DeleteAsync($"comments/{commentId}/like"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/ICommentsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/ICommentsService.cs new file mode 100644 index 000000000..c8cd617aa --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Comments/ICommentsService.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Comments.CommandsDto; +using Astravent.Web.Wasm.DTO.Comments; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Comments +{ + public interface ICommentsService + { + Task> SearchRootCommentsAsync(SearchRootCommentsCommand command); + Task>> SearchSubCommentsAsync(SearchSubCommentsCommand command); + Task GetCommentAsync(Guid commentId); + Task> CreateCommentAsync(CreateCommentCommand command); + Task> UpdateCommentAsync(UpdateCommentCommand command); + Task DeleteCommentAsync(Guid commentId); + Task> AddLikeAsync(AddLikeDto command); + Task DeleteLikeAsync(Guid commentId); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/ChatSignalRService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/ChatSignalRService.cs new file mode 100644 index 000000000..1dbac44dc --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/ChatSignalRService.cs @@ -0,0 +1,179 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.SignalR.Client; +using Microsoft.JSInterop; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.DTO.Communication; +using System; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Communication +{ + public class ChatSignalRService : IAsyncDisposable + { + private HubConnection _hubConnection; + private readonly NavigationManager _navigationManager; + private readonly IIdentityService _identityService; + private Guid _userId; + private Guid _currentChatId; + private bool _disposed; + + public event Action MessageReceived; + public event Action MessageStatusUpdated; + public event Action TypingNotificationReceived; + public event Action ConnectionChanged; + + public ChatSignalRService(NavigationManager navigationManager, IIdentityService identityService) + { + _navigationManager = navigationManager; + _identityService = identityService; + } + + public async Task StartAsync(Guid userId, Guid currentChatId) + { + if (_hubConnection != null && _hubConnection.State == HubConnectionState.Connected) + { + await _hubConnection.StopAsync(); + } + + _userId = userId; + _currentChatId = currentChatId; + var hubUrl = $"http://localhost:5016/chatHub?userId={userId}&chatId={currentChatId}"; + + _hubConnection = new HubConnectionBuilder() + .WithUrl(hubUrl, options => + { + options.AccessTokenProvider = async () => + { + if (_disposed) + { + return null; + } + + try + { + return await _identityService.GetAccessTokenAsync(); + } + catch (JSDisconnectedException) + { + return null; + } + }; + }) + .WithAutomaticReconnect() + .Build(); + + RegisterHubEvents(); + await StartConnectionAsync(); + } + + public async Task StartAsync(Guid userId) + { + if (_hubConnection != null && _hubConnection.State == HubConnectionState.Connected) + { + await _hubConnection.StopAsync(); + } + + _userId = userId; + _currentChatId = Guid.Empty; + var hubUrl = $"http://localhost:5016/chatHub?userId={userId}"; + + _hubConnection = new HubConnectionBuilder() + .WithUrl(hubUrl, options => + { + options.AccessTokenProvider = async () => + { + if (_disposed) + { + return null; + } + + try + { + return await _identityService.GetAccessTokenAsync(); + } + catch (JSDisconnectedException) + { + return null; + } + }; + }) + .WithAutomaticReconnect() + .Build(); + + RegisterHubEvents(); + await StartConnectionAsync(); + } + + private void RegisterHubEvents() + { + _hubConnection.On("ReceiveMessage", (jsonMessage) => + { + var message = System.Text.Json.JsonSerializer.Deserialize(jsonMessage); + MessageReceived?.Invoke(message); + }); + + _hubConnection.On("ReceiveMessageStatusUpdate", (jsonStatusUpdate) => + { + var statusUpdate = System.Text.Json.JsonSerializer.Deserialize(jsonStatusUpdate); + MessageStatusUpdated?.Invoke(Guid.Parse(statusUpdate.MessageId), statusUpdate.Status); + }); + + _hubConnection.On("ReceiveTypingNotification", (userId, isTyping) => + { + TypingNotificationReceived?.Invoke(userId, isTyping); + }); + + _hubConnection.Reconnecting += (error) => + { + ConnectionChanged?.Invoke(false); + return Task.CompletedTask; + }; + + _hubConnection.Reconnected += (connectionId) => + { + ConnectionChanged?.Invoke(true); + return Task.CompletedTask; + }; + + _hubConnection.Closed += (error) => + { + ConnectionChanged?.Invoke(false); + return Task.CompletedTask; + }; + } + + private async Task StartConnectionAsync() + { + if (!_disposed) + { + await _hubConnection.StartAsync(); + ConnectionChanged?.Invoke(true); + } + } + + public async Task StopAsync() + { + if (_hubConnection != null && _hubConnection.State != HubConnectionState.Disconnected) + { + await _hubConnection.StopAsync(); + } + } + + public async Task SendTypingNotificationAsync(bool isTyping) + { + if (_hubConnection.State == HubConnectionState.Connected) + { + await _hubConnection.InvokeAsync("SendTypingNotification", _currentChatId.ToString(), _userId.ToString(), isTyping); + } + } + + public async ValueTask DisposeAsync() + { + if (_hubConnection != null) + { + await _hubConnection.DisposeAsync(); + } + _disposed = true; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/AddUserToChatCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/AddUserToChatCommand.cs new file mode 100644 index 000000000..9fe089dc0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/AddUserToChatCommand.cs @@ -0,0 +1,16 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Communication.CommandsDto +{ + public class AddUserToChatCommand + { + public Guid ChatId { get; set; } + public Guid UserId { get; set; } + + public AddUserToChatCommand(Guid chatId, Guid userId) + { + ChatId = chatId; + UserId = userId; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/CreateChatCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/CreateChatCommand.cs new file mode 100644 index 000000000..b6a9fd300 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/CreateChatCommand.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.Areas.Communication.CommandsDto +{ + public class CreateChatCommand + { + public Guid ChatId { get; set; } + public List ParticipantIds { get; set; } + public string ChatName { get; set; } + + public CreateChatCommand(Guid chatId, List participantIds, string chatName = null) + { + ChatId = chatId; + ParticipantIds = participantIds ?? new List(); + ChatName = chatName; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/DeleteChatCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/DeleteChatCommand.cs new file mode 100644 index 000000000..a175538f4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/DeleteChatCommand.cs @@ -0,0 +1,16 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Communication.CommandsDto +{ + public class DeleteChatCommand + { + public Guid ChatId { get; set; } + public Guid UserId { get; set; } + + public DeleteChatCommand(Guid chatId, Guid userId) + { + ChatId = chatId; + UserId = userId; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/DeleteMessageCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/DeleteMessageCommand.cs new file mode 100644 index 000000000..195912473 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/DeleteMessageCommand.cs @@ -0,0 +1,16 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Communication.CommandsDto +{ + public class DeleteMessageCommand + { + public Guid MessageId { get; set; } + public Guid ChatId { get; set; } + + public DeleteMessageCommand(Guid messageId, Guid chatId) + { + MessageId = messageId; + ChatId = chatId; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/RemoveUserFromChatCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/RemoveUserFromChatCommand.cs new file mode 100644 index 000000000..f0b030c02 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/RemoveUserFromChatCommand.cs @@ -0,0 +1,16 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Communication.CommandsDto +{ + public class RemoveUserFromChatCommand + { + public Guid ChatId { get; set; } + public Guid UserId { get; set; } + + public RemoveUserFromChatCommand(Guid chatId, Guid userId) + { + ChatId = chatId; + UserId = userId; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/SendMessageCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/SendMessageCommand.cs new file mode 100644 index 000000000..24b128513 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/SendMessageCommand.cs @@ -0,0 +1,20 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Communication.CommandsDto +{ + public class SendMessageCommand + { + public Guid ChatId { get; set; } + public Guid SenderId { get; set; } + public string Content { get; set; } + public string MessageType { get; set; } + + public SendMessageCommand(Guid chatId, Guid senderId, string content, string messageType = "Text") + { + ChatId = chatId; + SenderId = senderId; + Content = content; + MessageType = messageType; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/UpdateMessageStatusCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/UpdateMessageStatusCommand.cs new file mode 100644 index 000000000..6f41d31c5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommandsDto/UpdateMessageStatusCommand.cs @@ -0,0 +1,18 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Communication.CommandsDto +{ + public class UpdateMessageStatusCommand + { + public Guid ChatId { get; set; } + public Guid MessageId { get; set; } + public string Status { get; set; } + + public UpdateMessageStatusCommand(Guid chatId, Guid messageId, string status) + { + ChatId = chatId; + MessageId = messageId; + Status = status; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommunicationService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommunicationService.cs new file mode 100644 index 000000000..c66c1db7a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/CommunicationService.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.HttpClients; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.DTO.Communication; +using Astravent.Web.Wasm.Areas.Communication.CommandsDto; +using System.Linq; + +namespace Astravent.Web.Wasm.Areas.Communication +{ + public class CommunicationService : ICommunicationService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public CommunicationService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public async Task> GetUserChatsAsync(Guid userId, int page, int pageSize) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return await _httpClient.GetAsync>($"communication/chats/user/{userId}?page={page}&pageSize={pageSize}"); + } + + public async Task FindExistingChatAsync(Guid userId, Guid friendId) + { + var userChatsResponse = await GetUserChatsAsync(userId, 1, 100); + + if (userChatsResponse == null || !userChatsResponse.Items.Any()) + return null; + + // Loop through all the chats to find one with the friend + foreach (var userChat in userChatsResponse.Items.SelectMany(u => u.Chats)) + { + if (userChat.ParticipantIds.Contains(friendId)) + { + // Return the chat if a matching participant is found + return userChat; + } + } + + // Return null if no existing chat is found + return null; + } + + + public async Task GetChatByIdAsync(Guid chatId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return await _httpClient.GetAsync($"communication/chats/{chatId}"); + } + + public async Task> GetMessagesForChatAsync(Guid chatId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return await _httpClient.GetAsync>($"communication/chats/{chatId}/messages"); + } + + public async Task> CreateChatAsync(CreateChatCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return await _httpClient.PostAsync("communication/chats", command); + } + + public async Task AddUserToChatAsync(Guid chatId, Guid userId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + await _httpClient.PutAsync($"communication/chats/{chatId}/users", new { chatId, userId }); + } + + public async Task DeleteChatAsync(Guid chatId, Guid userId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var command = new DeleteChatCommand(chatId, userId); + await _httpClient.DeleteAsync($"communication/chats/{chatId}/{userId}", command); + } + + + public async Task> SendMessageAsync(SendMessageCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return await _httpClient.PostAsync($"communication/chats/{command.ChatId}/messages", command); + } + + public async Task> UpdateMessageStatusAsync(UpdateMessageStatusCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return await _httpClient.PutAsync($"communication/chats/{command.ChatId}/messages/{command.MessageId}/status", command); + } + + public async Task DeleteMessageAsync(Guid chatId, Guid messageId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + await _httpClient.DeleteAsync($"communication/chats/{chatId}/messages/{messageId}"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/ICommunicationService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/ICommunicationService.cs new file mode 100644 index 000000000..d8ad7dd2c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Communication/ICommunicationService.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Communication.CommandsDto; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Communication; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Communication +{ + public interface ICommunicationService + { + Task> GetUserChatsAsync(Guid userId, int page, int pageSize); + Task FindExistingChatAsync(Guid userId, Guid friendId); + Task GetChatByIdAsync(Guid chatId); + Task> GetMessagesForChatAsync(Guid chatId); + Task> CreateChatAsync(CreateChatCommand command); + Task AddUserToChatAsync(Guid chatId, Guid userId); + Task DeleteChatAsync(Guid chatId, Guid userId); + Task> SendMessageAsync(SendMessageCommand command); + Task> UpdateMessageStatusAsync(UpdateMessageStatusCommand command); + Task DeleteMessageAsync(Guid chatId, Guid messageId); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CancelInterestInEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CancelInterestInEventCommand.cs new file mode 100644 index 000000000..f33197425 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CancelInterestInEventCommand.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Events; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class CancelSignUpToEventCommand + { + public Guid EventId { get; set; } + public Guid StudentId { get; set; } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CancelSignUpToEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CancelSignUpToEventCommand.cs new file mode 100644 index 000000000..a0ccfc251 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CancelSignUpToEventCommand.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Events; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class CancelInterestInEventCommand + { + public Guid EventId { get; set; } + public Guid StudentId { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CreateEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CreateEventCommand.cs new file mode 100644 index 000000000..87a530727 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/CreateEventCommand.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Events; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class CreateEventCommand + { + public Guid EventId { get; set; } = Guid.NewGuid(); + public string Name { get; set; } = string.Empty; + public string OrganizerType { get; set; } + public Guid OrganizerId { get; set; } + public Guid? OrganizationId { get; set; } + public string StartDate { get; set; } + public string EndDate { get; set; } + public string BuildingName { get; set; } = string.Empty; + public string Street { get; set; } = string.Empty; + public string BuildingNumber { get; set; } = string.Empty; + public string ApartmentNumber { get; set; } = string.Empty; + public string City { get; set; } = string.Empty; + public string ZipCode { get; set; } = string.Empty; + public string Country { get; set; } = string.Empty; + public IEnumerable MediaFilesUrl { get; set; } = new List(); + public string BannerUrl { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + public int Capacity { get; set; } + public decimal Fee { get; set; } + public string Category { get; set; } + public string PublishDate { get; set; } = string.Empty; + public string Visibility { get; set; } + + public EventSettingsDto Settings { get; set; } = new EventSettingsDto(); + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/EventSettings.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/EventSettings.cs new file mode 100644 index 000000000..0c5771027 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/EventSettings.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Astravent.Web.Wasm.DTO.Enums; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class EventSettings + { + public bool RequiresApproval { get; set; } + public bool IsOnlineEvent { get; set; } + public bool IsPrivate { get; set; } + public bool RequiresRSVP { get; set; } + public bool AllowsGuests { get; set; } + public bool ShowAttendeesPublicly { get; set; } + public bool SendReminders { get; set; } + public int ReminderDaysBefore { get; set; } + public bool EnableChat { get; set; } + public bool AllowComments { get; set; } + + public bool RequiresPayment { get; set; } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public PaymentMethod PaymentMethod { get; set; } + + public string PaymentReceiverDetails { get; set; } + public string PaymentGateway { get; set; } + public bool IssueTickets { get; set; } + public int MaxTicketsPerPerson { get; set; } + public decimal TicketPrice { get; set; } + + public bool RecordEvent { get; set; } + + public string CustomTermsAndConditions { get; set; } + public IDictionary CustomFields { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/EventSettingsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/EventSettingsDto.cs new file mode 100644 index 000000000..bbf2c11ff --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/EventSettingsDto.cs @@ -0,0 +1,37 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Astravent.Web.Wasm.DTO.Enums; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class EventSettingsDto + { + public bool RequiresApproval { get; set; } + public bool IsOnlineEvent { get; set; } + public bool IsPrivate { get; set; } + public bool RequiresRSVP { get; set; } + public bool AllowsGuests { get; set; } + public bool ShowAttendeesPublicly { get; set; } + public bool SendReminders { get; set; } + public int ReminderDaysBefore { get; set; } + public bool EnableChat { get; set; } + public bool AllowComments { get; set; } + + public bool RequiresPayment { get; set; } + + public string PaymentMethod { get; set; } // This is a string + + public string PaymentReceiverDetails { get; set; } + public string PaymentGateway { get; set; } + public bool IssueTickets { get; set; } + public int MaxTicketsPerPerson { get; set; } + public decimal TicketPrice { get; set; } + + public bool RecordEvent { get; set; } + + public string CustomTermsAndConditions { get; set; } + public IDictionary CustomFields { get; set; } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/RateEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/RateEventCommand.cs new file mode 100644 index 000000000..4fbdc3cc1 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/RateEventCommand.cs @@ -0,0 +1,11 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class RateEventCommand + { + public Guid EventId { get; set; } + public int Rating { get; set; } + public Guid StudentId { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SearchEvents.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SearchEvents.cs new file mode 100644 index 000000000..8e7a38a7f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SearchEvents.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class SearchEvents + { + public string Name { get; set; } + public string Organizer { get; set; } + public Guid OrganizationId { get; set; } + public string Category { get; set; } + public string State { get; set; } + public IEnumerable Friends { get; set; } + public string FriendsEngagementType { get; set; } + public string DateFrom { get; set; } + public string DateTo { get; set; } + public PageableDto Pageable { get; set; } + + public override string ToString() + { + var queryParams = new List(); + + if (!string.IsNullOrEmpty(Name)) queryParams.Add($"Name={Name}"); + if (!string.IsNullOrEmpty(Organizer)) queryParams.Add($"Organizer={Organizer}"); + if (OrganizationId != Guid.Empty) queryParams.Add($"OrganizationId={OrganizationId}"); + if (!string.IsNullOrEmpty(Category)) queryParams.Add($"Category={Category}"); + if (!string.IsNullOrEmpty(State)) queryParams.Add($"State={State}"); + if (Friends != null && Friends.Any()) queryParams.Add($"Friends={string.Join(",", Friends)}"); + if (!string.IsNullOrEmpty(FriendsEngagementType)) queryParams.Add($"FriendsEngagementType={FriendsEngagementType}"); + if (!string.IsNullOrEmpty(DateFrom)) queryParams.Add($"DateFrom={DateFrom}"); + if (!string.IsNullOrEmpty(DateTo)) queryParams.Add($"DateTo={DateTo}"); + if (Pageable != null) queryParams.Add($"Pageable.Page={Pageable.Page}&Pageable.Size={Pageable.Size}"); + + return string.Join("&", queryParams); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SearchOrganizerEvents.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SearchOrganizerEvents.cs new file mode 100644 index 000000000..08e711ed0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SearchOrganizerEvents.cs @@ -0,0 +1,15 @@ +using System; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class SearchOrganizerEvents + { + public Guid OrganizerId { get; set; } + public string Name { get; set; } + public string State { get; set; } + public string DateFrom { get; set; } + public string DateTo { get; set; } + public PageableDto Pageable { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/ShowInterestInEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/ShowInterestInEventCommand.cs new file mode 100644 index 000000000..637fafdff --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/ShowInterestInEventCommand.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Events; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class ShowInterestInEventCommand + { + public Guid EventId { get; set; } + public Guid StudentId { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SignUpToEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SignUpToEventCommand.cs new file mode 100644 index 000000000..27dbf671d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/SignUpToEventCommand.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Events; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class SignUpToEventCommand + { + public Guid EventId { get; set; } + public Guid StudentId { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/UpdateEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/UpdateEventCommand.cs new file mode 100644 index 000000000..84896cf44 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/UpdateEventCommand.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Events; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class UpdateEventCommand + { + public Guid EventId { get; set; } + public string Name { get; set; } + public string OrganizerType { get; set; } + public Guid OrganizerId { get; set; } + public Guid OrganizationId { get; set; } + public string StartDate { get; set; } + public string EndDate { get; set; } + public string BuildingName { get; set; } + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public string Country { get; set; } + public IEnumerable MediaFilesUrl { get; set; } + public string BannerUrl { get; set; } + public string Description { get; set; } + public int Capacity { get; set; } + public decimal Fee { get; set; } + public Category Category { get; set; } + public string PublishDate { get; set; } + public EventVisibility Visibility { get; set; } + public EventSettings Settings { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/ViewEventCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/ViewEventCommand.cs new file mode 100644 index 000000000..e0b69a61f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/CommandsDto/ViewEventCommand.cs @@ -0,0 +1,16 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Events.CommandsDto +{ + public class ViewEventCommand + { + public Guid UserId { get; set; } + public Guid EventId { get; set; } + + public ViewEventCommand(Guid userId, Guid eventId) + { + UserId = userId; + EventId = eventId; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/EventsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/EventsService.cs new file mode 100644 index 000000000..eb869c85a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/EventsService.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.Areas.Events.CommandsDto; +using Astravent.Web.Wasm.HttpClients; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.Areas.PagedResult; +using System.Web; +using System.Linq; +using Blazorise; + +namespace Astravent.Web.Wasm.Areas.Events +{ + public class EventsService : IEventsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public EventsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public async Task GetEventAsync(Guid eventId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"events/{eventId}"); + } + + public async Task> CreateEventAsync(CreateEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.PostAsync("events", command); + } + + public async Task> UpdateEventAsync(UpdateEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.PutAsync($"events/{command.EventId}", command); + } + + public async Task DeleteEventAsync(Guid eventId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.DeleteAsync($"events/{eventId}"); + } + + public async Task SignUpToEventAsync(SignUpToEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PostAsync($"events/{command.EventId}/sign-up", command); + } + + public async Task CancelSignUpToEventAsync(CancelSignUpToEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.DeleteAsync($"events/{command.EventId}/sign-up", command); + } + + public async Task ShowInterestInEventAsync(ShowInterestInEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PostAsync($"events/{command.EventId}/show-interest", command); + } + + public async Task CancelInterestInEventAsync(CancelInterestInEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.DeleteAsync($"events/{command.EventId}/show-interest", command); + } + + public async Task RateEventAsync(RateEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PostAsync($"events/{command.EventId}/rate", command); + } + + public async Task GetEventRatingAsync(Guid eventId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"events/{eventId}/rating"); + } + + public async Task> SearchEventsAsync(SearchEvents command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var queryParams = new List(); + + if (!string.IsNullOrEmpty(command.Name)) queryParams.Add($"Name={HttpUtility.UrlEncode(command.Name)}"); + if (!string.IsNullOrEmpty(command.Organizer)) queryParams.Add($"Organizer={HttpUtility.UrlEncode(command.Organizer)}"); + if (command.OrganizationId != Guid.Empty) queryParams.Add($"OrganizationId={command.OrganizationId}"); + if (!string.IsNullOrEmpty(command.Category)) queryParams.Add($"Category={HttpUtility.UrlEncode(command.Category)}"); + if (!string.IsNullOrEmpty(command.State)) queryParams.Add($"State={HttpUtility.UrlEncode(command.State)}"); + if (command.Friends != null && command.Friends.Any()) queryParams.Add($"Friends={string.Join(",", command.Friends)}"); + if (!string.IsNullOrEmpty(command.FriendsEngagementType)) queryParams.Add($"FriendsEngagementType={HttpUtility.UrlEncode(command.FriendsEngagementType)}"); + if (!string.IsNullOrEmpty(command.DateFrom)) queryParams.Add($"DateFrom={HttpUtility.UrlEncode(command.DateFrom)}"); + if (!string.IsNullOrEmpty(command.DateTo)) queryParams.Add($"DateTo={HttpUtility.UrlEncode(command.DateTo)}"); + + if (command.Pageable != null) + { + queryParams.Add($"Page={command.Pageable.Page}"); + queryParams.Add($"Size={command.Pageable.Size}"); + if (command.Pageable.Sort != null) + { + if (command.Pageable.Sort.SortBy != null && command.Pageable.Sort.SortBy.Any()) + { + queryParams.Add($"SortBy={HttpUtility.UrlEncode(string.Join(",", command.Pageable.Sort.SortBy))}"); + } + if (!string.IsNullOrEmpty(command.Pageable.Sort.Direction)) + { + queryParams.Add($"Direction={HttpUtility.UrlEncode(command.Pageable.Sort.Direction)}"); + } + } + } + + var queryString = "?" + string.Join("&", queryParams); + + return await _httpClient.GetAsync>($"events/search{queryString}"); + } + + + + public async Task>>> SearchOrganizerEventsAsync(SearchOrganizerEvents command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.PostAsync>>("events/search/organizer", command); + } + + public async Task GetEventParticipantsAsync(Guid eventId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"events/{eventId}/participants"); + } + + public async Task AddEventParticipantAsync(Guid eventId, Guid studentId, string studentName) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PostAsync($"events/{eventId}/participants", new {eventId, studentId, studentName}); + } + + public async Task RemoveEventParticipantAsync(Guid eventId, Guid participantId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.DeleteAsync($"events/{eventId}/participants?participantId={participantId}"); + } + + public async Task> GetPaginatedEventsAsync(int page, int pageSize) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync>($"events/paginated?page={page}&pageSize={pageSize}"); + } + public async Task> GetMyEventsAsync(Guid organizerId, int page, int pageSize) + { + return await _httpClient.GetAsync>($"events/organizer/{organizerId}/paginated?page={page}&pageSize={pageSize}"); + } + + public async Task> GetUserEventsAsync(Guid userId, int page, int pageSize, string engagementType) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync>($"events/users/{userId}?engagementType={engagementType}&page={page}&pageSize={pageSize}"); + } + + public async Task> GetUserEventsFeedAsync(Guid userId, int pageNumber, int pageSize, string sortBy, string direction) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var queryString = $"?pageNumber={pageNumber}&pageSize={pageSize}&sortBy={HttpUtility.UrlEncode(sortBy)}&direction={HttpUtility.UrlEncode(direction)}"; + return await _httpClient.GetAsync>($"events/users/{userId}/feed{queryString}"); + } + + public async Task ViewEventAsync(ViewEventCommand command) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PostAsync($"events/{command.EventId}/view", command); + } + + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/IEventsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/IEventsService.cs new file mode 100644 index 000000000..d21feb39e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Events/IEventsService.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.Areas.Events.CommandsDto; +using Astravent.Web.Wasm.HttpClients; +using Astravent.Web.Wasm.Areas.PagedResult; + +namespace Astravent.Web.Wasm.Areas.Events +{ + public interface IEventsService + { + Task GetEventAsync(Guid eventId); + Task> CreateEventAsync(CreateEventCommand command); + Task> UpdateEventAsync(UpdateEventCommand command); + Task DeleteEventAsync(Guid eventId); + Task SignUpToEventAsync(SignUpToEventCommand command); + Task CancelSignUpToEventAsync(CancelSignUpToEventCommand command); + Task ShowInterestInEventAsync(ShowInterestInEventCommand command); + Task CancelInterestInEventAsync(CancelInterestInEventCommand command); + Task RateEventAsync(RateEventCommand command); + Task GetEventRatingAsync(Guid eventId); + + // Task>>> SearchEventsAsync(SearchEvents command); + Task> SearchEventsAsync(SearchEvents command); + Task>>> SearchOrganizerEventsAsync(SearchOrganizerEvents command); + Task GetEventParticipantsAsync(Guid eventId); + Task AddEventParticipantAsync(Guid eventId, Guid studentId, string studentName); + Task RemoveEventParticipantAsync(Guid eventId, Guid participantId); + Task> GetPaginatedEventsAsync(int page, int pageSize); + Task> GetMyEventsAsync(Guid organizerId, int page, int pageSize); + Task> GetUserEventsAsync(Guid userId, int page, int pageSize, string engagementType); + Task> GetUserEventsFeedAsync(Guid userId, int pageNumber, int pageSize, string sortBy, string direction); + Task ViewEventAsync(ViewEventCommand command); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/AddFriendRequestDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/AddFriendRequestDto.cs new file mode 100644 index 000000000..f4ca380ab --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/AddFriendRequestDto.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Friends.CommandsDto +{ + public class AddFriendRequestDto + { + public Guid FriendId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/FriendRequestActionDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/FriendRequestActionDto.cs new file mode 100644 index 000000000..0d56b95a8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/FriendRequestActionDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Friends.CommandsDto +{ + public class FriendRequestActionDto + { + public Guid RequestId { get; set; } + public Guid RequesterId { get; set; } + public Guid FriendId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/WithdrawFriendRequestDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/WithdrawFriendRequestDto.cs new file mode 100644 index 000000000..f7ae38eb0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/CommandsDto/WithdrawFriendRequestDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Friends.CommandsDto +{ + public class WithdrawFriendRequestDto + { + public Guid InviterId { get; set; } + public Guid InviteeId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/FriendsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/FriendsService.cs new file mode 100644 index 000000000..fb2c7050f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/FriendsService.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.Areas.Friends.CommandsDto; +using Astravent.Web.Wasm.Areas.PagedResult; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.HttpClients; +using Astravent.Web.Wasm.DTO.Friends; + +namespace Astravent.Web.Wasm.Areas.Friends +{ + public class FriendsService : IFriendsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public FriendDto FriendDto { get; private set; } + + public FriendsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public async Task UpdateFriendDto(Guid friendId) + { + FriendDto = await GetFriendAsync(friendId); + } + + public void ClearFriendDto() + { + FriendDto = null; + } + + public async Task GetFriendAsync(Guid friendId) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"friends/{friendId}"); + } + + public async Task> GetAllFriendsAsync(Guid userId, int page = 1, int pageSize = 10) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + string url = $"friends/{userId}?page={page}&pageSize={pageSize}"; + var userFriends = await _httpClient.GetAsync>(url); + + var allFriends = userFriends.Items.SelectMany(uf => uf.Friends).ToList(); + + foreach (var friend in allFriends) + { + friend.StudentDetails = await GetStudentAsync(friend.FriendId); + } + + return new PagedResult(allFriends, userFriends.Page, userFriends.PageSize, userFriends.TotalItems); + } + + public async Task> AddFriendAsync(Guid friendId) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var payload = new AddFriendRequestDto { FriendId = friendId }; + return await _httpClient.PostAsync("friends", payload); + } + + public async Task RemoveFriendAsync(Guid friendId) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var requesterId = _identityService.GetCurrentUserId(); + + if (requesterId == Guid.Empty) + { + return; // Optionally handle the case where the requester ID is invalid + } + + await _httpClient.DeleteAsync($"friends/{requesterId}/{friendId}/remove"); + } + + // New method to get all students without pagination or filters + public async Task> GetAllStudentsAsync() + { + if (_httpClient == null) throw new InvalidOperationException("HTTP client is not initialized."); + string accessToken = await _identityService.GetAccessTokenAsync(); + if (string.IsNullOrEmpty(accessToken)) + throw new InvalidOperationException("Invalid or missing access token."); + + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync>("students"); + } + + // New method to get paginated students with optional search term + public async Task> GetAllStudentsAsync(int page = 1, int pageSize = 10, string searchTerm = null) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + string url = $"students?page={page}&pageSize={pageSize}&searchTerm={searchTerm}"; + return await _httpClient.GetAsync>(url); + } + + public async Task GetStudentAsync(Guid studentId) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"students/{studentId}"); + } + + public async Task InviteStudent(Guid inviterId, Guid inviteeId) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var payload = new { inviterId = inviterId, inviteeId = inviteeId }; + await _httpClient.PostAsync>($"friends/{inviteeId}/invite", payload); + } + + public async Task> GetSentFriendRequestsAsync(int page = 1, int pageSize = 10) + { + var studentId = _identityService.GetCurrentUserId(); + if (studentId == Guid.Empty) return new PagedResult(Enumerable.Empty(), page, pageSize, 0); + + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + string url = $"friends/requests/sent/{studentId}?page={page}&pageSize={pageSize}"; + var studentRequests = await _httpClient.GetAsync>(url); + + if (studentRequests == null || !studentRequests.Items.Any()) + { + return new PagedResult(Enumerable.Empty(), page, pageSize, 0); + } + + var friendRequests = studentRequests.Items.SelectMany(request => request.FriendRequests).ToList(); + + foreach (var request in friendRequests) + { + var userDetails = await GetStudentAsync(request.InviteeId); + request.InviteeName = $"{userDetails.FirstName} {userDetails.LastName}"; + request.InviteeEmail = userDetails.Email; + } + + return new PagedResult(friendRequests, studentRequests.Page, studentRequests.PageSize, studentRequests.TotalItems); + } + + public async Task> GetIncomingFriendRequestsAsync(int page = 1, int pageSize = 10) + { + var userId = _identityService.GetCurrentUserId(); + if (userId == Guid.Empty) return new PagedResult(Enumerable.Empty(), page, pageSize, 0); + + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + string url = $"friends/requests/{userId}?page={page}&pageSize={pageSize}"; + var studentRequests = await _httpClient.GetAsync>(url); + + if (studentRequests == null || !studentRequests.Items.Any()) + { + return new PagedResult(Enumerable.Empty(), page, pageSize, 0); + } + + var incomingRequests = studentRequests.Items.SelectMany(request => request.FriendRequests).ToList(); + + foreach (var request in incomingRequests) + { + var userDetails = await GetStudentAsync(request.InviterId); + request.InviterName = $"{userDetails.FirstName} {userDetails.LastName}"; + request.InviterEmail = userDetails.Email; + } + + return new PagedResult(incomingRequests, studentRequests.Page, studentRequests.PageSize, studentRequests.TotalItems); + } + + public async Task> GetPagedFollowersAsync(Guid userId, int page = 1, int pageSize = 10) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + string url = $"friends/{userId}/followers?page={page}&pageSize={pageSize}"; + var userFollowers = await _httpClient.GetAsync>(url); + + var allFollowers = userFollowers.Items.SelectMany(uf => uf.Friends).ToList(); + + foreach (var follower in allFollowers) + { + follower.StudentDetails = await GetStudentAsync(follower.UserId); + } + + return new PagedResult(allFollowers, userFollowers.Page, userFollowers.PageSize, userFollowers.TotalItems); + } + + public async Task> GetPagedFollowingAsync(Guid userId, int page = 1, int pageSize = 10) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + string url = $"friends/{userId}/following?page={page}&pageSize={pageSize}"; + var userFollowing = await _httpClient.GetAsync>(url); + + var allFollowing = userFollowing.Items.SelectMany(uf => uf.Friends).ToList(); + + foreach (var following in allFollowing) + { + following.StudentDetails = await GetStudentAsync(following.FriendId); + } + + return new PagedResult(allFollowing, userFollowing.Page, userFollowing.PageSize, userFollowing.TotalItems); + } + + public async Task AcceptFriendRequestAsync(FriendRequestActionDto requestAction) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PostAsync($"friends/requests/{requestAction.RequestId}/accept", requestAction); + } + + public async Task DeclineFriendRequestAsync(FriendRequestActionDto requestAction) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PostAsync($"friends/requests/{requestAction.RequestId}/decline", requestAction); + } + + public async Task WithdrawFriendRequestAsync(WithdrawFriendRequestDto withdrawRequest) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PutAsync($"friends/requests/{withdrawRequest.InviteeId}/withdraw", withdrawRequest); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/IFriendsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/IFriendsService.cs new file mode 100644 index 000000000..3f27d8c42 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Friends/IFriendsService.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Friends.CommandsDto; +using Astravent.Web.Wasm.Areas.PagedResult; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Friends; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Friends +{ + public interface IFriendsService + { + FriendDto FriendDto { get; } + + Task UpdateFriendDto(Guid friendId); + + void ClearFriendDto(); + + Task GetFriendAsync(Guid friendId); + + Task> GetAllFriendsAsync(Guid userId, int page = 1, int pageSize = 10); + + Task> GetPagedFollowersAsync(Guid userId, int page = 1, int pageSize = 10); + + Task> GetPagedFollowingAsync(Guid userId, int page = 1, int pageSize = 10); + + Task> AddFriendAsync(Guid friendId); + + Task RemoveFriendAsync(Guid friendId); + + Task GetStudentAsync(Guid studentId); + + Task> GetAllStudentsAsync(); + + Task> GetAllStudentsAsync(int page = 1, int pageSize = 10, string searchTerm = null); + + Task InviteStudent(Guid inviterId, Guid inviteeId); + + Task> GetSentFriendRequestsAsync(int page = 1, int pageSize = 10); + + Task> GetIncomingFriendRequestsAsync(int page = 1, int pageSize = 10); + + Task AcceptFriendRequestAsync(FriendRequestActionDto requestAction); + + Task DeclineFriendRequestAsync(FriendRequestActionDto requestAction); + + Task WithdrawFriendRequestAsync(WithdrawFriendRequestDto withdrawRequest); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Http/ErrorMapperService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Http/ErrorMapperService.cs new file mode 100644 index 000000000..a9fd83f06 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Http/ErrorMapperService.cs @@ -0,0 +1,24 @@ +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Http +{ + public class ErrorMapperService : IErrorMapperService + { + public string MapError(ErrorMessage error) + { + switch (error.Code) + { + case "invalid_credentials": + return "Failed to sign in. Please check your credentials and try again."; + case "email_in_use": + return $"{error.Reason} Please try again with a different email."; + case "invalid_event_date_time": + return "Please review the selected dates. They should be in chronological order."; + case "invalid_student_date_of_birth": + return "Please review the student's date of birth. It should be in the past."; + default: + return "Something went wrong. Please try again later."; + } + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Http/IErrorMapperService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Http/IErrorMapperService.cs new file mode 100644 index 000000000..856e0a476 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Http/IErrorMapperService.cs @@ -0,0 +1,10 @@ +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Http +{ + public interface IErrorMapperService + { + string MapError(ErrorMessage error); + } +} + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/CustomAuthenticationStateProvider.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/CustomAuthenticationStateProvider.cs new file mode 100644 index 000000000..68a06032c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/CustomAuthenticationStateProvider.cs @@ -0,0 +1,87 @@ +using Blazored.LocalStorage; +using System.Security.Claims; +using Microsoft.AspNetCore.Components.Authorization; +using System.IdentityModel.Tokens.Jwt; +using System; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO; +using System.Text.Json; +using Microsoft.IdentityModel.JsonWebTokens; +using Astravent.Web.Wasm.Areas.Identity; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.Areas.Identity +{ + public class CustomAuthenticationStateProvider : AuthenticationStateProvider + { + private readonly ILocalStorageService _localStorage; + private readonly JwtSecurityTokenHandler _jwtHandler = new JwtSecurityTokenHandler(); + private readonly IIdentityService _identityService; + + public CustomAuthenticationStateProvider(ILocalStorageService localStorage, IIdentityService identityService) + { + _localStorage = localStorage; + _identityService = identityService; + InitializeAsync(); + } + + public override async Task GetAuthenticationStateAsync() + { + string storedToken = await _localStorage.GetItemAsStringAsync("jwtDto"); + if (!string.IsNullOrEmpty(storedToken)) + { + var jwtDto = JsonSerializer.Deserialize(storedToken); + if (jwtDto != null && ValidateToken(jwtDto.AccessToken)) + { + var claims = ParseClaimsFromJwt(jwtDto.AccessToken); + var identity = new ClaimsIdentity(claims, "jwtAuthType"); + var user = new ClaimsPrincipal(identity); + return new AuthenticationState(user); + } + else if (jwtDto != null && !string.IsNullOrEmpty(jwtDto.RefreshToken)) + { + try + { + var newJwtDto = await _identityService.RefreshAccessToken(jwtDto.RefreshToken); + if (newJwtDto != null) + { + var newJwtDtoJson = JsonSerializer.Serialize(newJwtDto); + await _localStorage.SetItemAsStringAsync("jwtDto", newJwtDtoJson); + var claims = ParseClaimsFromJwt(newJwtDto.AccessToken); + var identity = new ClaimsIdentity(claims, "jwtAuthType"); + var user = new ClaimsPrincipal(identity); + return new AuthenticationState(user); + } + } + catch + { + await _localStorage.RemoveItemAsync("jwtDto"); + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + } + } + } + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + } + + + private IEnumerable ParseClaimsFromJwt(string jwt) + { + var token = _jwtHandler.ReadJwtToken(jwt); + return token.Claims; + } + + private bool ValidateToken(string jwt) + { + if (string.IsNullOrEmpty(jwt)) return false; + + var token = _jwtHandler.ReadJwtToken(jwt); + return token.ValidTo > DateTime.UtcNow; + } + + private async Task InitializeAsync() + { + var authState = await GetAuthenticationStateAsync(); + NotifyAuthenticationStateChanged(Task.FromResult(authState)); + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/GenerateTwoFactorSecretResponse.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/GenerateTwoFactorSecretResponse.cs new file mode 100644 index 000000000..fde27d589 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/GenerateTwoFactorSecretResponse.cs @@ -0,0 +1,5 @@ + +public class GenerateTwoFactorSecretResponse +{ + public string Secret { get; set; } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IIdentityService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IIdentityService.cs new file mode 100644 index 000000000..e169376cf --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IIdentityService.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Identity +{ + public interface IIdentityService + { + public JwtDto JwtDto { get; set;} + public UserDto UserDto { get; set; } + bool IsAuthenticated { get; set; } + Task GetAccountAsync(JwtDto jwtDto); + Task> SignUpAsync(string firstName, string lastName, string email, string password, string role = "user", IEnumerable permissions = null); + Task> SignInAsync(string email, string password, string deviceType); + Task Logout(); + Task GetAccessTokenAsync(); + Task RefreshAccessToken(string refreshToken); + Task InitializeAuthenticationState(); + Task CheckIfUserIsAuthenticated(); + Task IsTokenValid(); + public Guid GetCurrentUserId(); + Task GetCurrentUserIdFromJwtAsync(); + public string GetCurrentUserRole(); + + Task GrantOrganizerRightsAsync(Guid userId); + Task RevokeOrganizerRightsAsync(Guid userId); + Task BanUserAsync(Guid userId); + Task UnbanUserAsync(Guid userId); + + Task ForgotPasswordAsync(string email); + Task> ResetPasswordAsync(string token, string email, string newPassword); + Task> VerifyEmailAsync(string token, string email, string hashedToken); + Task GenerateTwoFactorSecretAsync(Guid userId); + + Task EnableTwoFactorAsync(Guid userId, string secret); + Task DisableTwoFactorAsync(Guid userId); + Task> VerifyTwoFactorCodeAsync(Guid userId, string code, string deviceType); + Task> UpdateStatus(Guid userId, bool isOnline, string deviceType); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IdentityComponent.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IdentityComponent.cs new file mode 100644 index 000000000..a82c488c7 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IdentityComponent.cs @@ -0,0 +1,30 @@ +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Identity +{ + public class IdentityComponent + { + private readonly IIdentityService _identityService; + + public IdentityComponent(IIdentityService identityService) + { + _identityService = identityService; + } + + public Task OnInit() + { + return Task.CompletedTask; + } + + public Task SignUpAsync(string firstName, string lastName, string email, string password, string role = "user") + => _identityService.SignUpAsync(firstName, lastName, email, password, role); + + public Task> SignInAsync(string email, string password, string deviceType, string ipAddress) + => _identityService.SignInAsync(email, password, deviceType); + + public Task GetAccount(JwtDto jwtDto) + => _identityService.GetAccountAsync(jwtDto); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IdentityService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IdentityService.cs new file mode 100644 index 000000000..1254e6ef7 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Identity/IdentityService.cs @@ -0,0 +1,491 @@ +using System; +using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Text.Json; +using System.Threading.Tasks; +using Blazored.LocalStorage; +using Microsoft.AspNetCore.Components; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Identity +{ + class IdentityService : IIdentityService + { + private readonly IHttpClient _httpClient; + private readonly JwtSecurityTokenHandler _jwtHandler; + private readonly ILocalStorageService _localStorage; + private readonly NavigationManager _navigationManager; + + public JwtDto JwtDto { get; set; } + public UserDto UserDto { get; set; } + public string Name { get; private set; } + public string Email { get; private set; } + public bool IsAuthenticated { get; set; } + + public IdentityService(IHttpClient httpClient, ILocalStorageService localStorage, NavigationManager navigationManager) + { + _httpClient = httpClient; + _jwtHandler = new JwtSecurityTokenHandler(); + _localStorage = localStorage; + _navigationManager = navigationManager; + } + + public async Task GetAccountAsync(JwtDto jwtDto) + { + if (jwtDto == null || string.IsNullOrEmpty(jwtDto.AccessToken)) + throw new ArgumentNullException(nameof(jwtDto), "JWT DTO or Access Token is null"); + + _httpClient.SetAccessToken(jwtDto.AccessToken); + var response = await _httpClient.GetAsync("identity/me"); + Console.WriteLine($"User fetched successfully: {JsonSerializer.Serialize(response)}"); + return response; + } + + public async Task> SignUpAsync(string firstName, string lastName, string email, string password, string role = "user", + IEnumerable permissions = null) + { + return await _httpClient.PostAsync("identity/sign-up", + new { firstName, lastName, email, password, role, permissions }); + } + + public async Task> SignInAsync(string email, string password, string deviceType) + { + try +{ + var response = await _httpClient.PostAsync("identity/sign-in", + new { email, password, deviceType }); + + if (response.Content != null) + { + JwtDto = response.Content; + + Console.WriteLine(response); + + if (JwtDto.IsTwoFactorRequired) + { + return response; + } + + var jwtDtoJson = JsonSerializer.Serialize(JwtDto); + await _localStorage.SetItemAsStringAsync("jwtDto", jwtDtoJson); + + var jwtToken = _jwtHandler.ReadJwtToken(JwtDto.AccessToken); + var payload = jwtToken.Payload; + UserDto = await GetAccountAsync(JwtDto); + Name = payload.Claims.FirstOrDefault(c => c.Type == "name")?.Value; + Email = payload.Claims.FirstOrDefault(c => c.Type == "e-mail")?.Value; + IsAuthenticated = true; + } + else + { + throw new InvalidOperationException("Failed to sign in. No JWT token received."); + } + return response; + + } + catch (Exception ex) + { + Console.WriteLine("An error occurred during sign-in: " + ex.Message); + return null; + } + } + + public async Task Logout() + { + if (JwtDto != null && !string.IsNullOrEmpty(JwtDto.RefreshToken)) + { + await RevokeRefreshToken(JwtDto.RefreshToken); + } + await _localStorage.RemoveItemAsync("jwtDto"); + JwtDto = null; + UserDto = null; + Name = null; + Email = null; + IsAuthenticated = false; + _navigationManager.NavigateTo("signin", forceLoad: true); + } + + public async Task RefreshAccessToken(string refreshToken) + { + var payload = new { refreshToken }; + var response = await _httpClient.PostAsync("identity/refresh-tokens/use", payload); + if (response.ErrorMessage != null) + { + throw new InvalidOperationException($"Error refreshing token: {response.ErrorMessage.Reason}"); + } + + if (response.Content != null) + { + return response.Content; + } + + throw new InvalidOperationException("Failed to refresh token"); + } + + private async Task RevokeRefreshToken(string refreshToken) + { + var payload = new { refreshToken }; + var response = await _httpClient.PostAsync("identity/refresh-tokens/revoke", payload); + Console.WriteLine($"Refresh token revocation response: {response}"); + if (response.ErrorMessage != null) + { + Console.WriteLine($"Error revoking refresh token: {response.ErrorMessage.Reason}"); + throw new InvalidOperationException($"Error revoking refresh token: {response.ErrorMessage.Reason}"); + } + } + + public async Task GetAccessTokenAsync() +{ + // Retrieve the JWT DTO stored in local storage + var jwtDtoJson = await _localStorage.GetItemAsStringAsync("jwtDto"); + + // If the JWT is not empty, deserialize it + if (!string.IsNullOrEmpty(jwtDtoJson)) + { + JwtDto jwtDto = JsonSerializer.Deserialize(jwtDtoJson); + + // Check if the deserialized object is valid + if (jwtDto != null && !string.IsNullOrEmpty(jwtDto.AccessToken)) + { + var jwtToken = _jwtHandler.ReadJwtToken(jwtDto.AccessToken); + + // Check if the token is still valid + if (jwtToken.ValidTo > DateTime.UtcNow) + { + return jwtDto.AccessToken; // Token is valid, return it + } + else + { + // Token is expired, check for refresh token + if (!string.IsNullOrEmpty(jwtDto.RefreshToken)) + { + try + { + // Attempt to refresh the token using the refresh token + JwtDto newJwtDto = await RefreshAccessToken(jwtDto.RefreshToken); + + if (newJwtDto != null) + { + // Store the new JWT in local storage + var newJwtDtoJson = JsonSerializer.Serialize(newJwtDto); + await _localStorage.SetItemAsStringAsync("jwtDto", newJwtDtoJson); + + return newJwtDto.AccessToken; // Return the new access token + } + } + catch (Exception ex) + { + // If refresh fails, clear the local storage and navigate to sign-in + await _localStorage.RemoveItemAsync("jwtDto"); + _navigationManager.NavigateTo("signin", forceLoad: true); + throw new InvalidOperationException("Failed to refresh token: " + ex.Message); + } + } + + // If no valid token or refresh token, clear local storage and redirect + await _localStorage.RemoveItemAsync("jwtDto"); + _navigationManager.NavigateTo("signin", forceLoad: true); + throw new InvalidOperationException("Session expired, please login again."); + } + } + } + + // If no JWT found, throw an exception + throw new InvalidOperationException("Authentication required."); +} + + + public async Task InitializeAuthenticationState() + { + var jwtDtoJson = await _localStorage.GetItemAsStringAsync("jwtDto"); + if (!string.IsNullOrEmpty(jwtDtoJson)) + { + JwtDto = JsonSerializer.Deserialize(jwtDtoJson); + var tokenExpirationDateTime = DateTimeOffset.FromUnixTimeSeconds(JwtDto.Expires).UtcDateTime; + if (JwtDto != null && DateTime.UtcNow < tokenExpirationDateTime) + { + UserDto = await GetAccountAsync(JwtDto); + IsAuthenticated = UserDto != null; + } + else if (JwtDto != null && !string.IsNullOrEmpty(JwtDto.RefreshToken)) + { + JwtDto = await RefreshAccessToken(JwtDto.RefreshToken); + if (JwtDto != null) + { + jwtDtoJson = JsonSerializer.Serialize(JwtDto); + await _localStorage.SetItemAsStringAsync("jwtDto", jwtDtoJson); + UserDto = await GetAccountAsync(JwtDto); + IsAuthenticated = UserDto != null; + } + } + } + } + + public async Task CheckIfUserIsAuthenticated() + { + var jwtDtoJson = await _localStorage.GetItemAsStringAsync("jwtDto"); + if (!string.IsNullOrEmpty(jwtDtoJson)) + { + JwtDto jwtDto = JsonSerializer.Deserialize(jwtDtoJson); + if (jwtDto?.AccessToken != null) + { + try + { + var jwtToken = _jwtHandler.ReadJwtToken(jwtDto.AccessToken); + if (jwtToken.ValidTo > DateTime.UtcNow) + { + IsAuthenticated = true; + } + else + { + IsAuthenticated = await TryRefreshToken(jwtDto.RefreshToken); + } + } + catch (Exception ex) + { + Console.WriteLine($"Token validation error: {ex.Message}"); + IsAuthenticated = false; + } + } + else + { + Console.WriteLine("AccessToken is null"); + IsAuthenticated = false; + } + } + else + { + Console.WriteLine("jwtDtoJson is null or empty"); + IsAuthenticated = false; + } + return IsAuthenticated; + } + + private async Task TryRefreshToken(string refreshToken) + { + try + { + if (!string.IsNullOrEmpty(refreshToken)) + { + var response = await _httpClient.PostAsync("identity/refresh-tokens/use", new { refreshToken }); + if (response.Content != null) + { + var newJwtDtoJson = JsonSerializer.Serialize(response.Content); + await _localStorage.SetItemAsStringAsync("jwtDto", newJwtDtoJson); + JwtDto = response.Content; + return true; + } + } + else + { + Console.WriteLine("RefreshToken is null or empty"); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error refreshing token: {ex.Message}"); + await Logout(); + } + return false; + } + + public async Task IsTokenValid() + { + var jwtDtoJson = await _localStorage.GetItemAsStringAsync("jwtDto"); + if (!string.IsNullOrEmpty(jwtDtoJson)) + { + var jwtDto = JsonSerializer.Deserialize(jwtDtoJson); + if (jwtDto != null && !string.IsNullOrEmpty(jwtDto.AccessToken)) + { + var jwtToken = _jwtHandler.ReadJwtToken(jwtDto.AccessToken); + return jwtToken.ValidTo > DateTime.UtcNow; + } + else + { + Console.WriteLine("jwtDto or AccessToken is null"); + } + } + else + { + Console.WriteLine("jwtDtoJson is null or empty"); + } + return false; + } + + public Guid GetCurrentUserId() + { + if (UserDto != null && UserDto.Id != Guid.Empty) + { + Console.WriteLine($"User ID: {UserDto.Id}"); + return UserDto.Id; + } + else + { + Console.WriteLine("User ID is null or empty"); + return GetCurrentUserIdFromJwtAsync().GetAwaiter().GetResult(); + } + } + + public async Task GetCurrentUserIdFromJwtAsync() + { + string jwtTokenJson = await _localStorage.GetItemAsStringAsync("jwtDto"); + if (string.IsNullOrEmpty(jwtTokenJson)) + { + throw new InvalidOperationException("No JWT token found in local storage."); + } + + JwtDto jwtDto = JsonSerializer.Deserialize(jwtTokenJson); + if (jwtDto == null || string.IsNullOrEmpty(jwtDto.AccessToken)) + { + throw new InvalidOperationException("JWT token is invalid or missing."); + } + + var jwtToken = _jwtHandler.ReadJwtToken(jwtDto.AccessToken); + var userIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "sub"); + if (userIdClaim != null) + { + return Guid.Parse(userIdClaim.Value); + } + + throw new InvalidOperationException("User ID claim not found in JWT token."); + } + + + + + public string GetCurrentUserRole() + { + if (UserDto != null && UserDto.Id != Guid.Empty) + { + return UserDto.Role; + } + return string.Empty; + } + + public Task GrantOrganizerRightsAsync(Guid userId) + { + _httpClient.SetAccessToken(JwtDto.AccessToken); + return _httpClient.PostAsync($"identity/users/{userId}/organizer-rights", new { userId }); + } + + public Task RevokeOrganizerRightsAsync(Guid userId) + { + _httpClient.SetAccessToken(JwtDto.AccessToken); + return _httpClient.DeleteAsync($"identity/users/{userId}/organizer-rights"); + } + + public Task BanUserAsync(Guid userId) + { + _httpClient.SetAccessToken(JwtDto.AccessToken); + return _httpClient.PostAsync($"identity/users/{userId}/ban", new { userId }); + } + + public Task UnbanUserAsync(Guid userId) + { + _httpClient.SetAccessToken(JwtDto.AccessToken); + return _httpClient.DeleteAsync($"identity/users/{userId}/ban"); + } + + public async Task ForgotPasswordAsync(string email) + { + await _httpClient.PostAsync("identity/password/forgot", new { Email = email }); + } + + public async Task> ResetPasswordAsync(string token, string email, string newPassword) + { + Console.WriteLine($"Attempting to reset password with the following parameters:"); + Console.WriteLine($"Token: {token}"); + Console.WriteLine($"Email: {email}"); + Console.WriteLine($"NewPassword: {newPassword}"); + + try + { + // Decode token to extract user ID (pseudo-code, replace with your actual token decoding logic) + var userId = DecodeToken(token); + Console.WriteLine($"Decoded UserId: {userId}"); + + // Proceed with password reset + var response = await _httpClient.PostAsync("identity/password/reset", new { UserId = userId, Token = token, Email = email, NewPassword = newPassword }); + + return response; + } + catch (Exception ex) + { + Console.WriteLine($"Error during password reset: {ex.Message}"); + throw; // Rethrow the exception after logging + } + } + + private Guid DecodeToken(string token) + { + var handler = new JwtSecurityTokenHandler(); + var jwtToken = handler.ReadJwtToken(token); + var userIdClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "sub"); + return Guid.Parse(userIdClaim.Value); + } + + public async Task> VerifyEmailAsync(string token, string email, string hashedToken) + { + var response = await _httpClient.PostAsync("identity/email/verify", new { Token = token, Email = email, HashedToken = hashedToken }); + return response; + } + + public async Task GenerateTwoFactorSecretAsync(Guid userId) + { + _httpClient.SetAccessToken(JwtDto.AccessToken); + var response = await _httpClient.PostAsync("identity/2fa/generate-secret", new { UserId = userId }); + if (response.Content != null) + { + return response.Content.Secret; + } + throw new InvalidOperationException("Failed to generate two-factor secret."); + } + + public async Task EnableTwoFactorAsync(Guid userId, string secret) + { + await _httpClient.PostAsync("identity/2fa/enable", new { UserId = userId, Secret = secret }); + } + + public async Task DisableTwoFactorAsync(Guid userId) + { + await _httpClient.PostAsync("identity/2fa/disable", new { UserId = userId }); + } + + public async Task> VerifyTwoFactorCodeAsync(Guid userId, string code, string deviceType) + { + var payload = new { userId, code, deviceType }; + var response = await _httpClient.PostAsync("identity/2fa/verify-code", payload); + if (response.Content != null) + { + JwtDto = response.Content; + var jwtDtoJson = JsonSerializer.Serialize(JwtDto); + await _localStorage.SetItemAsStringAsync("jwtDto", jwtDtoJson); + + var jwtToken = _jwtHandler.ReadJwtToken(JwtDto.AccessToken); + var payloadClaims = jwtToken.Payload; + UserDto = await GetAccountAsync(JwtDto); + Name = payloadClaims.Claims.FirstOrDefault(c => c.Type == "name")?.Value; + Email = payloadClaims.Claims.FirstOrDefault(c => c.Type == "e-mail")?.Value; + IsAuthenticated = true; + } + return response; + } + + public async Task> UpdateStatus(Guid userId, bool isOnline, string deviceType) + { + var payload = new { userId, isOnline, deviceType}; + + var response = await _httpClient.PutAsync("identity/users/status", payload); + + if (response.ErrorMessage != null) + { + throw new InvalidOperationException($"Error updating user status: {response.ErrorMessage.Reason}"); + } + + return response; + } + + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/MediaFiles/IMediaFilesService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/MediaFiles/IMediaFilesService.cs new file mode 100644 index 000000000..0740948c4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/MediaFiles/IMediaFilesService.cs @@ -0,0 +1,41 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.Http; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.MediaFiles +{ + public interface IMediaFilesService + { + public Task GetFileAsync(Guid fileId); + public Task GetOriginalFileAsync(Guid fileId); + public Task> UploadMediaFileAsync( + Guid sourceId, + string sourceType, + Guid uploaderId, + string fileName, + string fileContentType, + byte[] fileData); + + Task> UploadFileAsync( + Guid sourceId, + string sourceType, + Guid uploaderId, + string fileName, + string fileContentType, + byte[] fileData); + + public Task> UploadOrganizationImageAsync( + Guid organizationId, + string sourceType, + Guid uploaderId, + string fileName, + string fileContentType, + byte[] fileData); + public Task DeleteMediaFileAsync(string fileUrl); + + + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/MediaFiles/MediaFilesService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/MediaFiles/MediaFilesService.cs new file mode 100644 index 000000000..7c7a9dafe --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/MediaFiles/MediaFilesService.cs @@ -0,0 +1,112 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.MediaFiles +{ + public class MediaFilesService : IMediaFilesService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public MediaFilesService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public Task GetFileAsync(Guid fileId) + { + return _httpClient.GetAsync($"media-files/{fileId}"); + } + + public Task GetOriginalFileAsync(Guid fileId) + { + return _httpClient.GetAsync($"media-files/{fileId}/original"); + } + + public Task> UploadMediaFileAsync( + Guid sourceId, + string sourceType, + Guid uploaderId, + string fileName, + string fileContentType, + byte[] fileData) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + + var requestBody = new + { + MediaFileId = Guid.NewGuid(), + SourceId = sourceId, + SourceType = sourceType, + UploaderId = uploaderId, + FileName = fileName, + FileContentType = fileContentType, + FileData = fileData + }; + + return _httpClient.PostAsync("media-files", requestBody); + } + + public Task> UploadFileAsync( + Guid sourceId, + string sourceType, + Guid uploaderId, + string fileName, + string fileContentType, + byte[] fileData) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + + var requestBody = new + { + FileId = Guid.NewGuid(), + SourceId = sourceId, + SourceType = sourceType, + UploaderId = uploaderId, + FileName = fileName, + FileContentType = fileContentType, + FileData = fileData + }; + + return _httpClient.PostAsync("media-files/files", requestBody); + } + + public Task DeleteMediaFileAsync(string fileUrl) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.DeleteAsync($"media-files/delete/{Uri.EscapeDataString(fileUrl)}", + new { MediaFileUrl = fileUrl }); + } + + public Task> UploadOrganizationImageAsync( + Guid organizationId, + string sourceType, + Guid uploaderId, + string fileName, + string fileContentType, + byte[] fileData) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + + var requestBody = new + { + OrganizationId = organizationId, + MediaFileId = Guid.NewGuid(), + SourceType = sourceType, + UploaderId = uploaderId, + FileName = fileName, + FileContentType = fileContentType, + FileData = fileData + }; + + return _httpClient.PostAsync("media-files", requestBody); + } + + + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/INotificationsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/INotificationsService.cs new file mode 100644 index 000000000..99ca7599a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/INotificationsService.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Notifications; + +namespace Astravent.Web.Wasm.Areas.Notifications +{ + public interface INotificationsService + { + Task> GetNotificationsByUserAsync(Guid userId, int page = 1, int pageSize = 10, string sortOrder = "desc"); + Task> GetNotificationsByUserAsync(Guid userId, int page = 1, int pageSize = 10, string sortOrder = "desc", string status = "Unread"); + + Task UpdateNotificationStatusAsync(Guid userId, Guid notificationId, string status); + + Task UpdateNotificationStatusAsync(Guid notificationId, bool isActive); + Task DeleteNotificationAsync(Guid userId, Guid notificationId); + Task GetNotificationByIdAsync(Guid userId, Guid notificationId); + Task CreateNotificationAsync(NotificationToUsersDto notification); + Task IsUserConnectedAsync(Guid userId); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/NotificationsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/NotificationsService.cs new file mode 100644 index 000000000..bebe2a92e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/NotificationsService.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.Data.Events; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; +using Astravent.Web.Wasm.DTO.Notifications; +using System.Collections.Concurrent; + +namespace Astravent.Web.Wasm.Areas.Notifications +{ + public class NotificationsService: INotificationsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + private static readonly ConcurrentDictionary ConnectedUsers = new(); + + + public NotificationsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public async Task> GetNotificationsByUserAsync(Guid userId, int page = 1, int pageSize = 10, string sortOrder = "desc") + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var url = $"notifications/{userId}?page={page}&pageSize={pageSize}&sortOrder={sortOrder}"; + + // Fetch the paginated response from the server + var response = await _httpClient.GetAsync>(url); + + return response; + } + + // public async Task CreateNotificationAsync(NotificationDto notification) + // { + // string accessToken = await _identityService.GetAccessTokenAsync(); + // _httpClient.SetAccessToken(accessToken); + // return await _httpClient.PostAsync("notifications", notification); + // } + + public async Task UpdateNotificationStatusAsync(Guid userId, Guid notificationId, string status) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var payload = new { UserId = userId, NotificationId = notificationId, Status = status }; + var url = $"notifications/{userId}/{notificationId}/status"; + await _httpClient.PutAsync(url, payload); + } + + public async Task UpdateNotificationStatusAsync(Guid notificationId, bool isActive) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + await _httpClient.PutAsync($"notifications/{notificationId}/status", new { IsActive = isActive }); + } + + public async Task DeleteNotificationAsync(Guid userId, Guid notificationId) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + var payload = new { UserId = userId, NotificationId = notificationId}; + _httpClient.SetAccessToken(accessToken); + var url = $"notifications/notification/{userId}/{notificationId}"; + await _httpClient.DeleteAsync(url, payload); + } + + + public async Task GetNotificationByIdAsync(Guid userId, Guid notificationId) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var url = $"notifications/{userId}/{notificationId}"; + return await _httpClient.GetAsync(url); + } + + public async Task> GetNotificationsByUserAsync(Guid userId, int page = 1, int pageSize = 20, string sortOrder = "desc", string status = "Unread") + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var url = $"notifications/{userId}?page={page}&pageSize={pageSize}&sortOrder={sortOrder}&status={status}"; + + var response = await _httpClient.GetAsync>(url); + + return response; + } + public async Task CreateNotificationAsync(NotificationToUsersDto notification) + { + string accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + var url = "notifications"; + await _httpClient.PostAsync>(url, notification); + } + + public void AddConnectedUser(Guid userId) + { + ConnectedUsers[userId] = true; + } + + // Method to remove a user from the connected users list + public void RemoveConnectedUser(Guid userId) + { + ConnectedUsers.TryRemove(userId, out _); + } + + // Method to check if a user is connected + public Task IsUserConnectedAsync(Guid userId) + { + return Task.FromResult(ConnectedUsers.ContainsKey(userId)); + } + + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/SignalRService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/SignalRService.cs new file mode 100644 index 000000000..900b0d273 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Notifications/SignalRService.cs @@ -0,0 +1,91 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.SignalR.Client; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.DTO.Notifications; +using System; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Notifications +{ + public class SignalRService : IAsyncDisposable + { + private HubConnection _hubConnection; + private readonly NavigationManager _navigationManager; + private readonly IIdentityService _identityService; + private Guid _userId; + + public event Action NotificationReceived; + + public SignalRService(NavigationManager navigationManager, IIdentityService identityService) + { + _navigationManager = navigationManager; + _identityService = identityService; + } + + public async Task StartAsync(Guid userId) + { + _userId = userId; + var hubUrl = $"http://localhost:5006/notificationHub?userId={userId}"; + + Console.WriteLine($"Initializing SignalR connection to URL: {hubUrl}"); + + _hubConnection = new HubConnectionBuilder() + .WithUrl(hubUrl, options => + { + options.AccessTokenProvider = async () => + { + var token = await _identityService.GetAccessTokenAsync(); + Console.WriteLine($"Using Access Token: {token}"); + return token; + }; + }) + .WithAutomaticReconnect() + .Build(); + + _hubConnection.On("ReceiveNotification", (jsonMessage) => + { + var notification = System.Text.Json.JsonSerializer.Deserialize(jsonMessage); + NotificationReceived?.Invoke(notification); + }); + + _hubConnection.Closed += async (error) => + { + Console.WriteLine($"Connection closed due to an error: {error?.Message}"); + await Task.Delay(new Random().Next(0, 5) * 1000); + await _hubConnection.StartAsync(); + }; + + try + { + await _hubConnection.StartAsync(); + Console.WriteLine("SignalR connection started successfully."); + } + catch (Exception ex) + { + Console.WriteLine($"Error starting SignalR connection: {ex.Message}"); + } + } + + public async Task StopAsync() + { + try + { + Console.WriteLine("Stopping SignalR connection..."); + await _hubConnection.StopAsync(); + Console.WriteLine("SignalR connection stopped successfully."); + } + catch (Exception ex) + { + Console.WriteLine($"Error stopping SignalR connection: {ex.Message}"); + } + } + + public async ValueTask DisposeAsync() + { + if (_hubConnection != null) + { + await _hubConnection.DisposeAsync(); + } + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/AcceptFollowRequestDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/AcceptFollowRequestDto.cs new file mode 100644 index 000000000..360070dd1 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/AcceptFollowRequestDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class AcceptFollowRequestDto + { + public Guid OrganizationId { get; set; } + public Guid RequestId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/AssignRoleToMemberCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/AssignRoleToMemberCommand.cs new file mode 100644 index 000000000..96e65754e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/AssignRoleToMemberCommand.cs @@ -0,0 +1,19 @@ +using System; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class AssignRoleToMemberCommand + { + public Guid OrganizationId { get; } + public Guid MemberId { get; } + public string Role { get; } + + public AssignRoleToMemberCommand(Guid organizationId, Guid memberId, string role) + { + OrganizationId = organizationId; + MemberId = memberId; + Role = role; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateOrganizationDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateOrganizationDto.cs new file mode 100644 index 000000000..a1c59a96d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateOrganizationDto.cs @@ -0,0 +1,18 @@ +using System; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class CreateOrganizationDto + { + public Guid OrganizationId { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public Guid? RootId { get; set; } + public Guid? ParentId { get; set; } = null; + public Guid OwnerId { get; set; } + public OrganizationSettingsDto Settings { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateOrganizationRoleCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateOrganizationRoleCommand.cs new file mode 100644 index 000000000..d3a2b466e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateOrganizationRoleCommand.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class CreateOrganizationRoleCommand + { + public Guid OrganizationId { get; } + public string RoleName { get; } + public string Description { get; } + public Dictionary Permissions { get; } + + public CreateOrganizationRoleCommand(Guid organizationId, string roleName, string description, Dictionary permissions) + { + OrganizationId = organizationId; + RoleName = roleName; + Description = description; + Permissions = permissions; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateSubOrganizationCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateSubOrganizationCommand.cs new file mode 100644 index 000000000..e5f4c226f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/CreateSubOrganizationCommand.cs @@ -0,0 +1,18 @@ +using System; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class CreateSubOrganizationCommand + { + public Guid SubOrganizationId { get; } + public string Name { get; } + public string Description { get; } + public Guid RootId { get; } + public Guid ParentId { get; } + public Guid OwnerId { get; } + public OrganizationSettingsDto Settings { get; } + public string BannerUrl { get; } + public string ImageUrl { get; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/FollowOrganizationDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/FollowOrganizationDto.cs new file mode 100644 index 000000000..a2a13d724 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/FollowOrganizationDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class FollowOrganizationDto + { + public Guid UserId { get; set; } + public Guid OrganizationId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/InviteUserToOrganizationCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/InviteUserToOrganizationCommand.cs new file mode 100644 index 000000000..067288970 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/InviteUserToOrganizationCommand.cs @@ -0,0 +1,17 @@ +using System; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class InviteUserToOrganizationCommand + { + public Guid OrganizationId { get; } + public Guid UserId { get; } + + public InviteUserToOrganizationCommand(Guid organizationId, Guid userId) + { + OrganizationId = organizationId; + UserId = userId; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/ManageFeedCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/ManageFeedCommand.cs new file mode 100644 index 000000000..4411018ea --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/ManageFeedCommand.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class ManageFeedCommand + { + public Guid OrganizationId { get; } + public string Content { get; } + public string Action { get; } + + public ManageFeedCommand(Guid organizationId, string content, string action) + { + OrganizationId = organizationId; + Content = content; + Action = action; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/RejectFollowRequestDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/RejectFollowRequestDto.cs new file mode 100644 index 000000000..cb2d576c7 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/RejectFollowRequestDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class RejectFollowRequestDto + { + public Guid OrganizationId { get; set; } + public Guid RequestId { get; set; } + public string Reason { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/SetOrganizationPrivacyCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/SetOrganizationPrivacyCommand.cs new file mode 100644 index 000000000..c794dceb6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/SetOrganizationPrivacyCommand.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class SetOrganizationPrivacyCommand + { + public Guid OrganizationId { get; } + public bool IsPrivate { get; } + + public SetOrganizationPrivacyCommand(Guid organizationId, bool isPrivate) + { + OrganizationId = organizationId; + IsPrivate = isPrivate; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/SetOrganizationVisibilityCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/SetOrganizationVisibilityCommand.cs new file mode 100644 index 000000000..1e7e5f044 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/SetOrganizationVisibilityCommand.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class SetOrganizationVisibilityCommand + { + public Guid OrganizationId { get; } + public bool IsVisible { get; } + + public SetOrganizationVisibilityCommand(Guid organizationId, bool isVisible) + { + OrganizationId = organizationId; + IsVisible = isVisible; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateOrganizationCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateOrganizationCommand.cs new file mode 100644 index 000000000..3bde26545 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateOrganizationCommand.cs @@ -0,0 +1,48 @@ +using System; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class UpdateOrganizationCommand + { + public Guid OrganizationId { get; private set; } + public string Name { get; } + public string Description { get; } + public Guid RootId { get; } + public Guid ParentId { get; } + public Guid OwnerId { get; } + public OrganizationSettingsDto Settings { get; } + public string BannerUrl { get; } + public string ImageUrl { get; } + public string DefaultRoleName { get; } + + public string Address { get; } + public string Country { get; } + public string City { get; } + public string Telephone { get; } + public string Email { get; } + + public UpdateOrganizationCommand(Guid organizationId, string name, string description, + Guid rootId, Guid parentId, Guid ownerId, OrganizationSettingsDto settings, + string bannerUrl, string imageUrl, string defaultRoleName, + string address, string country, string city, string telephone, string email) + { + OrganizationId = organizationId == Guid.Empty ? Guid.NewGuid() : organizationId; + Name = name; + Description = description; + RootId = rootId; + ParentId = parentId; + OwnerId = ownerId; + Settings = settings; + BannerUrl = bannerUrl; + ImageUrl = imageUrl; + DefaultRoleName = defaultRoleName; + + Address = address; + Country = country; + City = city; + Telephone = telephone; + Email = email; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateOrganizationSettingsCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateOrganizationSettingsCommand.cs new file mode 100644 index 000000000..525eec8a5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateOrganizationSettingsCommand.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class UpdateOrganizationSettingsCommand + { + public Guid OrganizationId { get; } + public OrganizationSettingsDto Settings { get; } + + public UpdateOrganizationSettingsCommand(Guid organizationId, OrganizationSettingsDto settings) + { + OrganizationId = organizationId; + Settings = settings; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateRolePermissionsCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateRolePermissionsCommand.cs new file mode 100644 index 000000000..be96a5856 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/CommandsDto/UpdateRolePermissionsCommand.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Areas.Organizations.CommandsDto +{ + public class UpdateRolePermissionsCommand + { + public Guid OrganizationId { get; } + public Guid RoleId { get; } + public string RoleName { get; set; } + public string Description { get; set; } + public Dictionary Permissions { get; } + + public UpdateRolePermissionsCommand(Guid organizationId, Guid roleId, string roleName, + string description, Dictionary permissions) + { + OrganizationId = organizationId; + RoleId = roleId; + RoleName = roleName; + Description = description; + Permissions = permissions; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/IOrganizationsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/IOrganizationsService.cs new file mode 100644 index 000000000..e4aae978d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/IOrganizationsService.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Organizations.CommandsDto; +using Astravent.Web.Wasm.Areas.PagedResult; +using Astravent.Web.Wasm.DTO.Organizations; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Organizations +{ + public interface IOrganizationsService + { + Task GetOrganizationAsync(Guid organizationId); + Task GetOrganizationDetailsAsync(Guid organizationId); + Task> GetRootOrganizationsAsync(); + Task> GetChildrenOrganizationsAsync(Guid organizationId, int page, int pageSize); + Task GetOrganizationWithGalleryAndUsersAsync(Guid organizationId); + Task> GetAllChildrenOrganizationsAsync(Guid organizationId); + Task> CreateOrganizationAsync(CreateOrganizationDto command); + Task> CreateSubOrganizationAsync(Guid parentOrganizationId, CreateSubOrganizationCommand command); + Task DeleteOrganizationAsync(Guid organizationId); + Task> CreateOrganizationRoleAsync(Guid organizationId, CreateOrganizationRoleCommand command); + Task InviteUserToOrganizationAsync(Guid organizationId, InviteUserToOrganizationCommand command); + Task AssignRoleToMemberAsync(Guid organizationId, Guid memberId, AssignRoleToMemberCommand command); + Task> UpdateRolePermissionsAsync(Guid organizationId, Guid roleId, UpdateRolePermissionsCommand command); + Task SetOrganizationPrivacyAsync(Guid organizationId, SetOrganizationPrivacyCommand command); + Task> UpdateOrganizationSettingsAsync(Guid organizationId, UpdateOrganizationSettingsCommand command); + Task SetOrganizationVisibilityAsync(Guid organizationId, SetOrganizationVisibilityCommand command); + Task ManageFeedAsync(Guid organizationId, ManageFeedCommand command); + Task> UpdateOrganizationAsync(Guid organizationId, UpdateOrganizationCommand command); + Task> GetPaginatedUserOrganizationsAsync(Guid userId, int page, int pageSize); + Task> GetOrganizationRolesAsync(Guid organizationId); + Task> GetPaginatedOrganizationsAsync(int page, int pageSize, string search = null); + Task FollowOrganizationAsync(Guid organizationId); + Task AcceptFollowRequestAsync(Guid organizationId, Guid requestId); + Task RejectFollowRequestAsync(Guid organizationId, Guid requestId, string reason); + Task> GetUserFollowedOrganizationsAsync(Guid userId); + Task> GetOrganizationRequestsAsync(Guid organizationId, int page, int pageSize); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/OrganizationsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/OrganizationsService.cs new file mode 100644 index 000000000..3ab4d4026 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Organizations/OrganizationsService.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.Areas.Organizations.CommandsDto; +using Astravent.Web.Wasm.Areas.PagedResult; +using Astravent.Web.Wasm.DTO.Organizations; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Organizations +{ + public class OrganizationsService : IOrganizationsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public OrganizationsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public Task GetOrganizationAsync(Guid organizationId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync($"organizations/{organizationId}"); + } + + public Task GetOrganizationDetailsAsync(Guid organizationId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync($"organizations/{organizationId}/details"); + } + + public Task> GetRootOrganizationsAsync() + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync>("organizations/root"); + } + + public async Task> GetChildrenOrganizationsAsync(Guid organizationId, int page, int pageSize) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var queryString = $"organizations/{organizationId}/children?page={page}&pageSize={pageSize}"; + return await _httpClient.GetAsync>(queryString); + } + + + public Task> GetAllChildrenOrganizationsAsync(Guid organizationId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync>($"organizations/{organizationId}/children/all"); + } + + public Task GetOrganizationWithGalleryAndUsersAsync(Guid organizationId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync($"organizations/{organizationId}/details/gallery-users"); + } + + public Task> CreateOrganizationAsync(CreateOrganizationDto command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync("organizations", command); + } + + public Task> CreateSubOrganizationAsync(Guid parentOrganizationId, CreateSubOrganizationCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"organizations/{parentOrganizationId}/children", command); + } + + public Task DeleteOrganizationAsync(Guid organizationId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.DeleteAsync($"organizations/{organizationId}"); + } + + public Task> CreateOrganizationRoleAsync(Guid organizationId, CreateOrganizationRoleCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"organizations/{organizationId}/roles", command); + } + + public Task InviteUserToOrganizationAsync(Guid organizationId, InviteUserToOrganizationCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"organizations/{organizationId}/invite", command); + } + + public Task AssignRoleToMemberAsync(Guid organizationId, Guid memberId, AssignRoleToMemberCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"organizations/{organizationId}/roles/{memberId}", command); + } + + public Task> UpdateRolePermissionsAsync(Guid organizationId, Guid roleId, UpdateRolePermissionsCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"organizations/{organizationId}/roles/{roleId}/permissions", command); + } + + public Task SetOrganizationPrivacyAsync(Guid organizationId, SetOrganizationPrivacyCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"organizations/{organizationId}/privacy", command); + } + + public Task> UpdateOrganizationSettingsAsync(Guid organizationId, UpdateOrganizationSettingsCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"organizations/{organizationId}/settings", command); + } + + public Task SetOrganizationVisibilityAsync(Guid organizationId, SetOrganizationVisibilityCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"organizations/{organizationId}/visibility", command); + } + + public Task ManageFeedAsync(Guid organizationId, ManageFeedCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"organizations/{organizationId}/feed", command); + } + + public Task> UpdateOrganizationAsync(Guid organizationId, UpdateOrganizationCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"organizations/{organizationId}", command); + } + + public async Task> GetPaginatedUserOrganizationsAsync(Guid userId, int page, int pageSize) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var queryString = $"organizations/users/{userId}/organizations?page={page}&pageSize={pageSize}"; + return await _httpClient.GetAsync>(queryString); + } + + public Task> GetOrganizationRolesAsync(Guid organizationId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync>($"organizations/{organizationId}/roles"); + } + + public Task> GetPaginatedOrganizationsAsync(int page, int pageSize, string search = null) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var queryString = $"organizations/paginated?page={page}&pageSize={pageSize}&search={search}"; + return _httpClient.GetAsync>(queryString); + } + + public Task FollowOrganizationAsync(Guid organizationId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var command = new FollowOrganizationDto + { + UserId = _identityService.UserDto.Id, + OrganizationId = organizationId + }; + return _httpClient.PostAsync($"organizations/{organizationId}/follow", command); + } + + public Task AcceptFollowRequestAsync(Guid organizationId, Guid requestId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var command = new AcceptFollowRequestDto + { + OrganizationId = organizationId, + RequestId = requestId + }; + return _httpClient.PutAsync($"organizations/{organizationId}/requests/{requestId}/accept", command); + } + + public Task RejectFollowRequestAsync(Guid organizationId, Guid requestId, string reason) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var command = new RejectFollowRequestDto + { + OrganizationId = organizationId, + RequestId = requestId, + Reason = reason + }; + return _httpClient.PutAsync($"organizations/{organizationId}/requests/{requestId}/reject", command); + } + + public Task> GetUserFollowedOrganizationsAsync(Guid userId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync>($"organizations/users/{userId}/organizations/follow"); + } + + public async Task> GetOrganizationRequestsAsync(Guid organizationId, int page, int pageSize) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + var queryString = $"organizations/{organizationId}/requests?page={page}&pageSize={pageSize}"; + return await _httpClient.GetAsync>(queryString); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/PagedResult/PagedResult.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/PagedResult/PagedResult.cs new file mode 100644 index 000000000..a81ae5eb7 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/PagedResult/PagedResult.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.PagedResult +{ + public class PagedResult + { + public IEnumerable Items { get; } + public int Page { get; } + public int PageSize { get; } + public int TotalItems { get; } + public int TotalPages => (int)Math.Ceiling((decimal)TotalItems / PageSize); + + public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null; + public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null; + + public PagedResult(IEnumerable items, int page, int pageSize, int totalItems) + { + Items = items; + Page = page; + PageSize = pageSize; + TotalItems = totalItems; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/CreatePostCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/CreatePostCommand.cs new file mode 100644 index 000000000..1737650a2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/CreatePostCommand.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Enums.Posts; + +namespace Astravent.Web.Wasm.Areas.Posts.CommandsDto +{ + public class CreatePostCommand + { + public Guid PostId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public string TextContent { get; set; } + public IEnumerable MediaFiles { get; set; } + public string State { get; set; } + public DateTime? PublishDate { get; set; } + public PostContext Context { get; set; } + public string Visibility { get; set; } + public string PostType { get; set; } + public string Title { get; set; } + public Guid? PageOwnerId { get; set; } + public string PageOwnerType { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/RepostCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/RepostCommand.cs new file mode 100644 index 000000000..fc5c93086 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/RepostCommand.cs @@ -0,0 +1,37 @@ +using System; +using Astravent.Web.Wasm.DTO.Enums.Posts; + +namespace Astravent.Web.Wasm.Areas.Posts.CommandsDto +{ + public class RepostCommand + { + public Guid OriginalPostId { get; set; } + public Guid RepostedPostId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public PostContext Context { get; set; } + public Guid? PageOwnerId { get; set; } + public string PageOwnerType { get; set; } + + public RepostCommand( + Guid originalPostId, + Guid repostedPostId, + Guid? userId, + Guid? organizationId, + Guid? eventId, + PostContext context, + Guid? pageOwnerId = null, + string pageOwnerType = "User") + { + OriginalPostId = originalPostId; + RepostedPostId = repostedPostId; + UserId = userId; + OrganizationId = organizationId; + EventId = eventId; + Context = context; + PageOwnerId = pageOwnerId; + PageOwnerType = pageOwnerType; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/UpdatePostCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/UpdatePostCommand.cs new file mode 100644 index 000000000..9a15589f5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/CommandsDto/UpdatePostCommand.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Enums.Posts; + +namespace Astravent.Web.Wasm.Areas.Posts.CommandsDto +{ + public class UpdatePostCommand + { + public Guid PostId { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public string TextContent { get; set; } + public IEnumerable MediaFiles { get; set; } + public string State { get; set; } + public DateTime? PublishDate { get; set; } + public PostContext Context { get; set; } + public string Visibility { get; set; } + public string PostType { get; set; } + public string Title { get; set; } + public Guid? PageOwnerId { get; set; } + public string PageOwnerType { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/IPostsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/IPostsService.cs new file mode 100644 index 000000000..4f05cfd81 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/IPostsService.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Posts.CommandsDto; +using Astravent.Web.Wasm.Data.Posts; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Posts; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Posts +{ + public interface IPostsService + { + Task GetPostAsync(Guid postId); + Task ChangePostStateAsync(Guid postId, string state, DateTime publishDate); + Task> CreatePostAsync(CreatePostCommand command); + Task> RepostPostAsync(RepostCommand command); + Task>> SearchPostsAsync(SearchPosts searchPosts); + Task>> GetUserFeedAsync(Guid userId, int pageNumber, + int pageSize, string sortBy = "PublishDate", string direction = "asc"); + + Task>> GetCurrentUserFeedAsync(int pageNumber, + int pageSize, string sortBy = "PublishDate", string direction = "asc"); + Task DeletePostAsync(Guid postId); + Task> GetPostsAsync(Guid eventId); + Task> UpdatePostAsync(UpdatePostCommand command); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/PostsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/PostsService.cs new file mode 100644 index 000000000..29c328b49 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Posts/PostsService.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Web; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.Areas.Posts.CommandsDto; +using Astravent.Web.Wasm.Data.Events; +using Astravent.Web.Wasm.Data.Posts; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Enums.Posts; +using Astravent.Web.Wasm.DTO.Posts; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Posts +{ + public class PostsService: IPostsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public PostsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public Task GetPostAsync(Guid postId) + { + return _httpClient.GetAsync($"posts/{postId}"); + } + + public Task ChangePostStateAsync(Guid postId, string state, DateTime publishDate) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"posts/{postId}/state/{state}", new {postId, state, publishDate}); + } + + public Task> CreatePostAsync(CreatePostCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync("posts", command); + } + + public Task> RepostPostAsync(RepostCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync("posts/repost", command); + } + + public async Task>> SearchPostsAsync(SearchPosts searchPosts) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + + var query = HttpUtility.ParseQueryString(string.Empty); + if (searchPosts.UserId.HasValue) + query["UserId"] = searchPosts.UserId.ToString(); + if (searchPosts.OrganizationId.HasValue) + query["OrganizationId"] = searchPosts.OrganizationId.ToString(); + if (searchPosts.EventId.HasValue) + query["EventId"] = searchPosts.EventId.ToString(); + + query["PageNumber"] = searchPosts.Pageable.Page.ToString(); + query["PageSize"] = searchPosts.Pageable.Size.ToString(); + if (searchPosts.Pageable.Sort?.SortBy != null) + query["SortBy"] = string.Join(",", searchPosts.Pageable.Sort.SortBy); + query["Direction"] = searchPosts.Pageable.Sort?.Direction; + + string queryString = query.ToString(); + string url = $"posts/search?{queryString}"; + + try + { + var result = await _httpClient.GetAsync>(url); + return new HttpResponse>(result); + } + catch (Exception ex) + { + return new HttpResponse>(new ErrorMessage + { + Code = ex.Message, + Reason = ex.Message + }); + } + } + + public async Task>> GetUserFeedAsync(Guid userId, int pageNumber, + int pageSize, string sortBy = "PublishDate", string direction = "asc") + { + var accessToken = await _identityService.GetAccessTokenAsync(); + + _httpClient.SetAccessToken(accessToken); + + var query = HttpUtility.ParseQueryString(string.Empty); + query["PageNumber"] = pageNumber.ToString(); + query["PageSize"] = pageSize.ToString(); + query["SortBy"] = sortBy; + query["Direction"] = direction; + + string queryString = query.ToString(); + string url = $"posts/users/{userId}/feed?{queryString}"; + + try + { + var result = await _httpClient.GetAsync>(url); + return new HttpResponse>(result); + } + catch (Exception ex) + { + return new HttpResponse>(new ErrorMessage + { + Code = ex.Message, + Reason = ex.Message + }); + } + } + + public async Task>> GetCurrentUserFeedAsync(int pageNumber, + int pageSize, string sortBy = "PublishDate", string direction = "asc") + { + Guid userId; + + try + { + userId = await _identityService.GetCurrentUserIdFromJwtAsync(); + if (userId == Guid.Empty) + { + throw new InvalidOperationException("User ID could not be retrieved."); + } + } + catch (Exception ex) + { + Console.WriteLine($"Failed to get current user ID: {ex.Message}"); + return new HttpResponse>(new ErrorMessage + { + Code = ex.Message, + Reason = "User not authenticated or ID could not be retrieved." + }); + } + + var accessToken = await _identityService.GetAccessTokenAsync(); + + _httpClient.SetAccessToken(accessToken); + + var query = HttpUtility.ParseQueryString(string.Empty); + query["PageNumber"] = pageNumber.ToString(); + query["PageSize"] = pageSize.ToString(); + query["SortBy"] = sortBy; + query["Direction"] = direction; + + string queryString = query.ToString(); + string url = $"posts/users/{userId}/feed?{queryString}"; + + try + { + var result = await _httpClient.GetAsync>(url); + return new HttpResponse>(result); + } + catch (Exception ex) + { + Console.WriteLine($"Error fetching user feed: {ex.Message}"); + return new HttpResponse>(new ErrorMessage + { + Code = ex.Message, + Reason = ex.Message + }); + } + } + + + + + public Task DeletePostAsync(Guid postId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.DeleteAsync($"posts/{postId}"); + } + + public Task> GetPostsAsync(Guid eventId) + { + return _httpClient.GetAsync>($"posts?eventId={eventId}"); + } + + public Task> UpdatePostAsync(UpdatePostCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"posts/{command.PostId}", command); + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/CommandDto/CreateReactionDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/CommandDto/CreateReactionDto.cs new file mode 100644 index 000000000..8c885e9b7 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/CommandDto/CreateReactionDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Reactions.CommandDto +{ + public class CreateReactionDto + { + public Guid ReactionId { get; set; } = Guid.NewGuid(); + public Guid UserId { get; set; } + public string ReactionType { get; set; } + // ReactionType := LoveIt || LikeIt || Wow || ItWasOkay || HateIt + public Guid ContentId { get; set; } + public string ContentType { get; set; } + // ContentType := Post || Event || Comment + public string TargetType { get; set; } + // ReactionTargetType := User || Organization + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/CommandDto/UpdateReactionDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/CommandDto/UpdateReactionDto.cs new file mode 100644 index 000000000..38054c409 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/CommandDto/UpdateReactionDto.cs @@ -0,0 +1,13 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Reactions.CommandDto +{ + public class UpdateReactionDto + { + public Guid ReactionId { get; set; } + public Guid UserId { get; set; } + public string NewReactionType { get; set; } + public string ContentType { get; set; } + public string TargetType { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/IReactionsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/IReactionsService.cs new file mode 100644 index 000000000..f35cdf627 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/IReactionsService.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Reactions.CommandDto; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Reactions +{ + public interface IReactionsService + { + Task> GetReactionsAsync(Guid contentId, ReactionContentType contentType); + Task GetReactionsSummaryAsync(Guid contentId, ReactionContentType contentType); + Task> GetReactionsSummariesAsync(IEnumerable contentIds, ReactionContentType contentType); + Task> CreateReactionAsync(CreateReactionDto command); + Task> UpdateReactionAsync(UpdateReactionDto command); + Task DeleteReactionAsync(Guid reactionId); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/ReactionsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/ReactionsService.cs new file mode 100644 index 000000000..356bfc46f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reactions/ReactionsService.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.Areas.Reactions.CommandDto; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Reactions +{ + public class ReactionsService : IReactionsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public ReactionsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public Task> GetReactionsAsync(Guid contentId, ReactionContentType contentType) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync>($"reactions?contentId={contentId}&contentType={contentType}"); + } + + public Task GetReactionsSummaryAsync(Guid contentId, ReactionContentType contentType) + { + return _httpClient.GetAsync($"reactions/summary?contentId={contentId}&contentType={contentType}"); + } + + public async Task> GetReactionsSummariesAsync(IEnumerable contentIds, ReactionContentType contentType) + { + var summaries = new Dictionary(); + foreach (var id in contentIds) + { + summaries[id] = await _httpClient.GetAsync($"reactions/summary?contentId={id}&contentType={contentType}"); + } + return summaries; + } + + + public Task> CreateReactionAsync(CreateReactionDto command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync("reactions", command); + } + + public Task> UpdateReactionAsync(UpdateReactionDto command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PutAsync($"reactions/{command.ReactionId}", command); + } + + + + public Task DeleteReactionAsync(Guid reactionId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.DeleteAsync($"reactions/{reactionId}"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/CommandsDto/CreateReportCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/CommandsDto/CreateReportCommand.cs new file mode 100644 index 000000000..e785dd41c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/CommandsDto/CreateReportCommand.cs @@ -0,0 +1,27 @@ +using System; + +namespace Astravent.Web.Wasm.Areas.Reports.CommandsDto +{ + public class CreateReportCommand + { + public Guid ReportId { get; } + public Guid IssuerId { get; set; } + public Guid TargetId { get; set; } + public Guid TargetOwnerId { get; set; } + public string ContextType { get; set; } + public string Category { get; set; } + public string Reason { get; private set; } + + public CreateReportCommand(Guid reportId, Guid issuerId, Guid targetId, Guid targetOwnerId, + string contextType, string category, string reason) + { + ReportId = reportId == Guid.Empty ? Guid.NewGuid() : reportId; + IssuerId = issuerId; + TargetId = targetId; + TargetOwnerId = targetOwnerId; + ContextType = contextType; + Category = category; + Reason = reason; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/IReportsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/IReportsService.cs new file mode 100644 index 000000000..63009e9fa --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/IReportsService.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Reports.CommandsDto; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Reports +{ + public interface IReportsService + { + Task>>> SearchReportsAsync( + IEnumerable contextTypes, IEnumerable states, Guid reviewerId, PageableDto pageable); + + Task> CreateReportAsync(CreateReportCommand command); + + Task DeleteReportAsync(Guid reportId); + + Task> CancelReportAsync(Guid reportId); + + Task> StartReportReviewAsync(Guid reportId, Guid reviewerId); + + Task> ResolveReportAsync(Guid reportId); + + Task> RejectReportAsync(Guid reportId); + + Task>>> GetStudentReportsAsync(Guid studentId, + int page, int results); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/ReportsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/ReportsService.cs new file mode 100644 index 000000000..e80fdc97f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Reports/ReportsService.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.Areas.Reports.CommandsDto; +using Astravent.Web.Wasm.Data.Reports; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Reports +{ + public class ReportsService : IReportsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + public ReportsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public Task>>> SearchReportsAsync( + IEnumerable contextTypes, IEnumerable states, Guid reviewerId, PageableDto pageable) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + + var queryParams = new List(); + + if (contextTypes != null) + { + foreach (var contextType in contextTypes) + { + queryParams.Add($"contextTypes={contextType}"); + } + } + + if (states != null) + { + foreach (var state in states) + { + queryParams.Add($"states={state}"); + } + } + + if (reviewerId != Guid.Empty) + { + queryParams.Add($"reviewerId={reviewerId}"); + } + + if (pageable != null) + { + queryParams.Add($"page={pageable.Page}"); + queryParams.Add($"size={pageable.Size}"); + } + + var queryString = string.Join("&", queryParams); + + return _httpClient.GetAsync>>>( + $"reports/search?{queryString}"); + } + + public Task> CreateReportAsync(CreateReportCommand command) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync("reports", command); + } + + public Task DeleteReportAsync(Guid reportId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.DeleteAsync($"reports/{reportId}"); + } + + public Task> CancelReportAsync(Guid reportId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"reports/{reportId}/cancel", new { reportId }); + } + + public Task> StartReportReviewAsync(Guid reportId, Guid reviewerId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"reports/{reportId}/start-review", + new { reportId, reviewerId }); + } + + public Task> ResolveReportAsync(Guid reportId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"reports/{reportId}/resolve", new { reportId }); + } + + public Task> RejectReportAsync(Guid reportId) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.PostAsync($"reports/{reportId}/reject", new { reportId }); + } + + public Task>>> GetStudentReportsAsync(Guid studentId, + int page, int results) + { + _httpClient.SetAccessToken(_identityService.JwtDto.AccessToken); + return _httpClient.GetAsync>>>( + $"reports/students/{studentId}?page={page}&results={results}"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/CommandsDto/ViewUserProfileCommand.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/CommandsDto/ViewUserProfileCommand.cs new file mode 100644 index 000000000..f90676d48 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/CommandsDto/ViewUserProfileCommand.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Areas.Students.CommandsDto +{ + public class ViewUserProfileCommand + { + public Guid UserId { get; } + public Guid UserProfileId { get; } + + public ViewUserProfileCommand(Guid userId, Guid userProfileId) + { + UserId = userId; + UserProfileId = userProfileId; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/IStudentsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/IStudentsService.cs new file mode 100644 index 000000000..9434fd7e0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/IStudentsService.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Interests; +using Astravent.Web.Wasm.DTO.Languages; +using Astravent.Web.Wasm.DTO.Users; +using Astravent.Web.Wasm.DTO.Views; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Students +{ + public interface IStudentsService + { + StudentDto StudentDto { get; } + Task UpdateStudentDto(Guid studentId); + void ClearStudentDto(); + Task GetStudentAsync(Guid studentId); + Task> GetStudentsAsync(); + Task UpdateStudentAsync( + Guid studentId, + string firstName, + string lastName, + string profileImageUrl, + string description, + bool emailNotifications, + string contactEmail, + IEnumerable languages, + IEnumerable interests, + bool enableTwoFactor, + bool disableTwoFactor, + string twoFactorSecret, + IEnumerable education, + IEnumerable work, + string phoneNumber, + string country, + string city, + DateTime? dateOfBirth); + public Task> CompleteStudentRegistrationAsync(Guid studentId, string profileImageUrl, + string description, DateTime dateOfBirth, bool emailNotifications, string contactEmail); + Task GetStudentStateAsync(Guid studentId); + + Task GetUserNotificationPreferencesAsync(Guid studentId); + Task UpdateUserNotificationPreferencesAsync(Guid studentId, NotificationPreferencesDto preferencesDto, bool emailNotifications); + Task GetStudentWithGalleryImagesAsync(Guid studentId); + Task UpdateUserSettingsAsync(Guid studentId, AvailableSettingsDto availableSettings); + Task GetUserSettingsAsync(Guid studentId); + Task UpdateStudentLanguagesAndInterestsAsync( + Guid studentId, + IEnumerable languages, + IEnumerable interests); + + Task IsUserOnlineAsync(Guid studentId); + + Task ViewUserProfileAsync(Guid userId, Guid userProfileId); + + Task> GetUserProfileViewsAsync(Guid userId, int pageNumber, int pageSize); + Task BlockUserAsync(Guid blockerId, Guid blockedUserId); + Task UnblockUserAsync(Guid blockerId, Guid blockedUserId); + Task> GetBlockedUsersAsync(Guid blockerId, int page, int resultsPerPage); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/StudentsService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/StudentsService.cs new file mode 100644 index 000000000..aa4aac3e1 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Areas/Students/StudentsService.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.Areas.Notifications; +using Astravent.Web.Wasm.Areas.Students.CommandsDto; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Interests; +using Astravent.Web.Wasm.DTO.Languages; +using Astravent.Web.Wasm.DTO.Users; +using Astravent.Web.Wasm.DTO.Views; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.HttpClients; + +namespace Astravent.Web.Wasm.Areas.Students +{ + public class StudentsService : IStudentsService + { + private readonly IHttpClient _httpClient; + private readonly IIdentityService _identityService; + + private readonly INotificationsService _notificationsService; + + public StudentDto StudentDto { get; private set; } + + public StudentsService(IHttpClient httpClient, IIdentityService identityService) + { + _httpClient = httpClient; + _identityService = identityService; + } + + public async Task UpdateStudentDto(Guid studentId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + StudentDto = await _httpClient.GetAsync($"students/{studentId}"); + } + + public void ClearStudentDto() + { + StudentDto = null; + } + + public async Task GetStudentAsync(Guid studentId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"students/{studentId}"); + } + + public async Task> GetStudentsAsync() + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync>("students"); + } + + public async Task UpdateStudentAsync( + Guid studentId, + string firstName, + string lastName, + string profileImageUrl, + string description, + bool emailNotifications, + string contactEmail, + IEnumerable languages, + IEnumerable interests, + bool enableTwoFactor, + bool disableTwoFactor, + string twoFactorSecret, + IEnumerable education, + IEnumerable work, + string phoneNumber, + string country, + string city, + DateTime? dateOfBirth) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var updateStudentData = new + { + studentId, + firstName, + lastName, + profileImageUrl, + description, + emailNotifications, + contactEmail, + languages = languages.ToList(), + interests = interests.ToList(), + enableTwoFactor, + disableTwoFactor, + twoFactorSecret, + education, + work, + phoneNumber, + country, + city, + dateOfBirth + }; + + var jsonData = JsonSerializer.Serialize(updateStudentData); + Console.WriteLine($"Sending UpdateStudent request: {jsonData}"); + + await _httpClient.PutAsync($"students/{studentId}", updateStudentData); + } + + public async Task GetUserNotificationPreferencesAsync(Guid studentId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"students/{studentId}/notifications"); + } + + public Task> CompleteStudentRegistrationAsync(Guid studentId, string profileImageUrl, string description, DateTime dateOfBirth, bool emailNotifications, string contactEmail) + => _httpClient.PostAsync("students", new { studentId, profileImageUrl, description, dateOfBirth, emailNotifications, contactEmail }); + + public async Task GetStudentStateAsync(Guid studentId) + { + var student = await GetStudentAsync(studentId); + return student != null ? student.State : "invalid"; + } + + public async Task UpdateUserNotificationPreferencesAsync(Guid studentId, NotificationPreferencesDto preferencesDto, bool emailNotifications) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var updatePreferencesData = new + { + studentId, + emailNotifications, + preferencesDto.AccountChanges, + preferencesDto.SystemLogin, + preferencesDto.NewEvent, + preferencesDto.InterestBasedEvents, + preferencesDto.EventNotifications, + preferencesDto.CommentsNotifications, + preferencesDto.PostsNotifications, + preferencesDto.FriendsNotifications + }; + + var jsonData = JsonSerializer.Serialize(updatePreferencesData); + Console.WriteLine($"Sending UpdateUserNotificationPreferences request: {jsonData}"); + + await _httpClient.PostAsync($"students/{studentId}/notifications", updatePreferencesData); + } + + public async Task GetStudentWithGalleryImagesAsync(Guid studentId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"students/{studentId}/gallery"); + } + + public async Task UpdateUserSettingsAsync(Guid studentId, AvailableSettingsDto availableSettings) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var updateUserSettingsData = new + { + studentId, + CreatedAtVisibility = availableSettings.CreatedAtVisibility.ToString(), + DateOfBirthVisibility = availableSettings.DateOfBirthVisibility.ToString(), + InterestedInEventsVisibility = availableSettings.InterestedInEventsVisibility.ToString(), + SignedUpEventsVisibility = availableSettings.SignedUpEventsVisibility.ToString(), + EducationVisibility = availableSettings.EducationVisibility.ToString(), + WorkPositionVisibility = availableSettings.WorkPositionVisibility.ToString(), + LanguagesVisibility = availableSettings.LanguagesVisibility.ToString(), + InterestsVisibility = availableSettings.InterestsVisibility.ToString(), + ContactEmailVisibility = availableSettings.ContactEmailVisibility.ToString(), + PhoneNumberVisibility = availableSettings.PhoneNumberVisibility.ToString(), + ProfileImageVisibility = availableSettings.ProfileImageVisibility.ToString(), + BannerImageVisibility = availableSettings.BannerImageVisibility.ToString(), + GalleryVisibility = availableSettings.GalleryVisibility.ToString(), + PreferredLanguage = availableSettings.PreferredLanguage.ToString(), + FrontendVersion = availableSettings.FrontendVersion.ToString() + }; + + await _httpClient.PutAsync($"students/{studentId}/settings", updateUserSettingsData); + } + + public async Task GetUserSettingsAsync(Guid studentId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + return await _httpClient.GetAsync($"students/{studentId}/settings"); + } + + public async Task UpdateStudentLanguagesAndInterestsAsync( + Guid studentId, + IEnumerable languages, + IEnumerable interests) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var updateData = new + { + languages = languages.ToList(), + interests = interests.ToList() + }; + + var jsonData = JsonSerializer.Serialize(updateData); + Console.WriteLine($"Sending UpdateStudentLanguagesAndInterests request: {jsonData}"); + + await _httpClient.PutAsync($"students/{studentId}/languages-and-interests", updateData); + } + + public async Task IsUserOnlineAsync(Guid studentId) + { + return await _notificationsService.IsUserConnectedAsync(studentId); + } + + public async Task ViewUserProfileAsync(Guid userId, Guid userProfileId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var command = new ViewUserProfileCommand(userId, userProfileId); + await _httpClient.PostAsync("students/profiles/users/{userProfileId}/view", command); + } + + public async Task> GetUserProfileViewsAsync(Guid userId, int pageNumber, int pageSize) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var queryString = $"?pageNumber={pageNumber}&pageSize={pageSize}"; + return await _httpClient.GetAsync>($"students/profiles/users/{userId}/views/paginated{queryString}"); + } + + public async Task BlockUserAsync(Guid blockerId, Guid blockedUserId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var command = new { blockerId, blockedUserId }; + await _httpClient.PostAsync($"students/{blockerId}/block-user/{blockedUserId}", command); + } + + public async Task UnblockUserAsync(Guid blockerId, Guid blockedUserId) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var command = new { blockerId, blockedUserId }; + await _httpClient.PostAsync($"students/{blockerId}/unblock-user/{blockedUserId}", command); + } + + public async Task> GetBlockedUsersAsync(Guid blockerId, int page, int resultsPerPage) + { + var accessToken = await _identityService.GetAccessTokenAsync(); + _httpClient.SetAccessToken(accessToken); + + var queryString = $"?page={page}&resultsPerPage={resultsPerPage}"; + return await _httpClient.GetAsync>($"students/{blockerId}/blocked-users{queryString}"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Astravent.Web.Wasm.csproj b/MiniSpace.Web/src/Astravent.Web.Wasm/Astravent.Web.Wasm.csproj new file mode 100644 index 000000000..8ff10782d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Astravent.Web.Wasm.csproj @@ -0,0 +1,48 @@ + + + + net8.0 + enable + enable + service-worker-assets.js + + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + + + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Astravent.Web.Wasm.sln b/MiniSpace.Web/src/Astravent.Web.Wasm/Astravent.Web.Wasm.sln new file mode 100644 index 000000000..a693fb674 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Astravent.Web.Wasm.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Astravent.Web.Wasm", "Astravent.Web.Wasm.csproj", "{736007B0-E2EA-4C2E-866E-EBE8C3231EF0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {736007B0-E2EA-4C2E-866E-EBE8C3231EF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {736007B0-E2EA-4C2E-866E-EBE8C3231EF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {736007B0-E2EA-4C2E-866E-EBE8C3231EF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {736007B0-E2EA-4C2E-866E-EBE8C3231EF0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7A5E48E0-09B8-436B-8ACC-532F5A7EF569} + EndGlobalSection +EndGlobal diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/AddressDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/AddressDto.cs new file mode 100644 index 000000000..63ec9e0f4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/AddressDto.cs @@ -0,0 +1,13 @@ +namespace Astravent.Web.Wasm.DTO +{ + public class AddressDto + { + public string BuildingName { get; set; } + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public string Country { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/AvailableSettingsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/AvailableSettingsDto.cs new file mode 100644 index 000000000..5d58ddbc3 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/AvailableSettingsDto.cs @@ -0,0 +1,21 @@ +namespace Astravent.Web.Wasm.DTO +{ + public class AvailableSettingsDto + { + public Visibility CreatedAtVisibility { get; set; } + public Visibility DateOfBirthVisibility { get; set; } + public Visibility InterestedInEventsVisibility { get; set; } + public Visibility SignedUpEventsVisibility { get; set; } + public Visibility EducationVisibility { get; set; } + public Visibility WorkPositionVisibility { get; set; } + public Visibility LanguagesVisibility { get; set; } + public Visibility InterestsVisibility { get; set; } + public Visibility ContactEmailVisibility { get; set; } + public Visibility PhoneNumberVisibility { get; set; } + public Visibility ProfileImageVisibility { get; set; } + public Visibility BannerImageVisibility { get; set; } + public Visibility GalleryVisibility { get; set; } + public PreferredLanguage PreferredLanguage { get; set; } + public FrontendVersion FrontendVersion { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ChangeProductImageModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ChangeProductImageModel.cs new file mode 100644 index 000000000..7b5015dea --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ChangeProductImageModel.cs @@ -0,0 +1,11 @@ + +using System.Collections.Generic; +using Microsoft.AspNetCore.Components.Forms; + +namespace Astravent.Web.Wasm.DTO +{ + public class ChangeProductImageModel + { + public IReadOnlyList? Files { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentContext.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentContext.cs new file mode 100644 index 000000000..819c0bcf7 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentContext.cs @@ -0,0 +1,10 @@ +namespace Astravent.Web.Wasm.DTO.Comments +{ + public enum CommentContext + { + UserPost, + UserEvent, + OrganizationPost, + OrganizationEvent + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentDto.cs new file mode 100644 index 000000000..c9e8f51c4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentDto.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Comments +{ + public class CommentDto + { + public Guid Id { get; set; } + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + public Guid UserId { get; set; } + public IEnumerable Likes { get; set; } + public Guid ParentId { get; set; } + public string TextContent { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime LastUpdatedAt { get; set; } + public DateTime LastReplyAt { get; set; } + public int RepliesCount { get; set; } + public bool IsDeleted { get; set; } + public IEnumerable Replies { get; set; } + + public bool CanExpand { get; set; } + public HashSet SubComments { get; set; } + public int SubCommentsPage { get; set; } + public CommentDto Parent { get; set; } + public bool IsLast { get; set; } + + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentExtensions.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentExtensions.cs new file mode 100644 index 000000000..c65cd6b40 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/CommentExtensions.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Comments +{ + public static class CommentExtensions + { + public static ReplyDto ToReplyDto(this CommentDto comment) + { + if (comment == null) + throw new ArgumentNullException(nameof(comment)); + + return new ReplyDto + { + Id = comment.Id, + ParentId = comment.ParentId, + UserId = comment.UserId, + TextContent = comment.TextContent, + CreatedAt = comment.CreatedAt, + IsDeleted = comment.IsDeleted + }; + } + + public static CommentDto ToCommentDto(this ReplyDto reply) + { + if (reply == null) + throw new ArgumentNullException(nameof(reply)); + + return new CommentDto + { + Id = reply.Id, + ParentId = reply.ParentId, + UserId = reply.UserId, + TextContent = reply.TextContent, + CreatedAt = reply.CreatedAt, + IsDeleted = reply.IsDeleted, + Replies = new List() + }; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/ReplyDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/ReplyDto.cs new file mode 100644 index 000000000..6fd9415b1 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Comments/ReplyDto.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Comments +{ + public class ReplyDto + { + public Guid Id { get; set; } + public Guid ParentId { get; set; } + public Guid UserId { get; set; } + public string TextContent { get; set; } + public DateTime CreatedAt { get; set; } + public bool IsDeleted { get; set; } + + public ReplyDto() + { + } + + public ReplyDto(ReplyDto reply) + { + Id = reply.Id; + ParentId = reply.ParentId; + UserId = reply.UserId; + TextContent = reply.TextContent; + CreatedAt = reply.CreatedAt; + IsDeleted = reply.IsDeleted; + } + + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/ChatDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/ChatDto.cs new file mode 100644 index 000000000..1057ecde4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/ChatDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Communication +{ + public class ChatDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public List ParticipantIds { get; set; } + public List Messages { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/MessageDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/MessageDto.cs new file mode 100644 index 000000000..8f1609952 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/MessageDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Communication +{ + public class MessageDto + { + public Guid Id { get; set; } + public Guid SenderId { get; set; } + public string Content { get; set; } + public Guid ChatId { get; set; } + public DateTime Timestamp { get; set; } + public string MessageType { get; set; } + public string Status { get; set; } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/MessageStatusUpdateDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/MessageStatusUpdateDto.cs new file mode 100644 index 000000000..574a3b13a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/MessageStatusUpdateDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Communication +{ + public class MessageStatusUpdateDto + { + public string ChatId { get; set; } + public string MessageId { get; set; } + public string Status { get; set; } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/UserChatDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/UserChatDto.cs new file mode 100644 index 000000000..42111e3e1 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Communication/UserChatDto.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Communication +{ + public class UserChatDto + { + public Guid UserId { get; set; } + public List Chats { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EducationDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EducationDto.cs new file mode 100644 index 000000000..aff4c09aa --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EducationDto.cs @@ -0,0 +1,13 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class EducationDto + { + public string InstitutionName { get; set; } + public string Degree { get; set; } + public DateTime? StartDate { get; set; } = null; + public DateTime? EndDate { get; set; } = null; + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/EventVisibility.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/EventVisibility.cs new file mode 100644 index 000000000..5b05d42c5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/EventVisibility.cs @@ -0,0 +1,9 @@ +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum EventVisibility + { + Public, + Private, + Unlisted + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/NotificationEventType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/NotificationEventType.cs new file mode 100644 index 000000000..23eb28ea5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/NotificationEventType.cs @@ -0,0 +1,36 @@ +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum NotificationEventType + { + NewFriendRequest, + FriendRequestAccepted, + NewEvent, + EventDeleted, + EventNewSignUp, + EventNewSignUpFriend, + StudentCancelledSignedUpToEvent, + StudentShowedInterestInEvent, + StudentCancelledInterestInEvent, + EventParticipantAdded, + EventParticipantRemoved, + PostCreated, + PostUpdated, + MentionedInPost, + EventReminder, + PasswordResetRequest, + UserSignUp, + NewEventInvitaion, + CommentCreated, + CommentUpdated, + ReactionAdded, + ReportCreated, + ReportDeleted, + ReportRejected, + ReportResolved, + ReportReviewStarted, + ReportCancelled, + Other, + EmailVerified, + TwoFactorCodeGenerated, + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/OrganizerType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/OrganizerType.cs new file mode 100644 index 000000000..32cac5284 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/OrganizerType.cs @@ -0,0 +1,8 @@ +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum OrganizerType + { + User, + Organization + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/PaymentMethod.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/PaymentMethod.cs new file mode 100644 index 000000000..fbf0efd58 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/PaymentMethod.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum PaymentMethod + { + Online, + Offline + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Permission.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Permission.cs new file mode 100644 index 000000000..20f23a4f5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Permission.cs @@ -0,0 +1,33 @@ +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum Permission + { + CreateSubGroups = 0, + DeleteSubGroups = 1, + EditOrganizationDetails = 2, + InviteUsers = 3, + RemoveMembers = 4, + ManageMembershipRequests = 5, + MakePosts = 6, + EditPosts = 7, + DeletePosts = 8, + CommentOnPosts = 9, + DeleteComments = 10, + MakeReactions = 11, + PinPosts = 12, + CreateEvents = 13, + EditEvents = 14, + DeleteEvents = 15, + ManageEventParticipation = 16, + AssignRoles = 17, + EditPermissions = 18, + ViewAuditLogs = 19, + SendMessageToAll = 20, + CreateCommunicationChannels = 21, + ManageFeed = 22, + ModerateContent = 23, + ModifyGallery = 24, + UpdateProfileImage = 25, + UpdateOrganizationImage = 26 + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Posts/PostContext.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Posts/PostContext.cs new file mode 100644 index 000000000..58b08917e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Posts/PostContext.cs @@ -0,0 +1,9 @@ +namespace Astravent.Web.Wasm.DTO.Enums.Posts +{ + public enum PostContext + { + UserPage, + OrganizationPage, + EventPage + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReactionContentType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReactionContentType.cs new file mode 100644 index 000000000..c48f70bac --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReactionContentType.cs @@ -0,0 +1,8 @@ +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum ReactionContentType + { + Event, + Post + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReactionType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReactionType.cs new file mode 100644 index 000000000..5339a0a80 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReactionType.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum ReactionType + { + LoveIt, + LikeIt, + Wow, + ItWasOkay, + HateIt + } + + public static class ReactionTypeExtensions + { + private const string Heart = @"heart"; + private const string Like = @"thumb-up"; + private const string Okay = @"emoticon-happy"; + private const string Wow = @"emoticon-lol"; + private const string Hate =@"emoticon-confused"; + + + public static string GetReactionText(this ReactionType reactionType) + { + return reactionType switch + { + ReactionType.LoveIt => "Love it!", + ReactionType.LikeIt => "Like it.", + ReactionType.Wow => "Wow!", + ReactionType.ItWasOkay => "It was okay.", + ReactionType.HateIt => "Hate it!", + _ => "No reactions!" + }; + } + + + public static string GetReactionIcon(this ReactionType? reactionType) + { + return reactionType switch + { + ReactionType.LoveIt => Heart, + ReactionType.LikeIt => Like, + ReactionType.Wow => Wow, + ReactionType.ItWasOkay => Okay, + ReactionType.HateIt => Hate, + _ => "no-reactions" + }; + } + + public static string GetReactionIcon(this ReactionType reactionType) + { + return reactionType switch + { + ReactionType.LoveIt => Heart, + ReactionType.LikeIt => Like, + ReactionType.Wow => Wow, + ReactionType.ItWasOkay => Okay, + ReactionType.HateIt => Hate, + _ => "no-reactions" + }; + } + + public static List> GenerateReactionPairs() + => [ + new KeyValuePair("", null), + new KeyValuePair(GetReactionText(ReactionType.LoveIt), ReactionType.LoveIt), + new KeyValuePair(GetReactionText(ReactionType.LikeIt), ReactionType.LikeIt), + new KeyValuePair(GetReactionText(ReactionType.Wow), ReactionType.Wow), + new KeyValuePair(GetReactionText(ReactionType.ItWasOkay), ReactionType.ItWasOkay), + new KeyValuePair(GetReactionText(ReactionType.HateIt), ReactionType.HateIt) + ]; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Reactions/ReactionTargetType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Reactions/ReactionTargetType.cs new file mode 100644 index 000000000..02cfa8e3c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/Reactions/ReactionTargetType.cs @@ -0,0 +1,8 @@ +namespace Astravent.Web.Wasm.DTO.Enums.Reactions +{ + public enum ReactionTargetType + { + User, + Organization + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReportCategory.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReportCategory.cs new file mode 100644 index 000000000..0f57fc440 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Enums/ReportCategory.cs @@ -0,0 +1,35 @@ +using System; + +namespace Astravent.Web.Wasm.DTO.Enums +{ + public enum ReportCategory + { + Spam, + HarassmentAndBullying, + Violence, + SexualContent, + Misinformation, + PrivacyViolations, + IntellectualPropertyViolations, + OtherViolations + } + + public static class ReportCategoryExtensions + { + public static string GetReportCategoryText(ReportCategory reportCategory) + { + return reportCategory switch + { + ReportCategory.Spam => "Spam", + ReportCategory.HarassmentAndBullying => "Harassment and bullying", + ReportCategory.Violence => "Violence", + ReportCategory.SexualContent => "Sexual content", + ReportCategory.Misinformation => "Misinformation", + ReportCategory.PrivacyViolations => "Privacy violations", + ReportCategory.IntellectualPropertyViolations => "Intellectual property violations", + ReportCategory.OtherViolations => "Other violations", + _ => throw new ArgumentOutOfRangeException(nameof(reportCategory), reportCategory, null) + }; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EventDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EventDto.cs new file mode 100644 index 000000000..9a466cc8e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EventDto.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.Areas.Events.CommandsDto; +using Astravent.Web.Wasm.DTO.Events; + +namespace Astravent.Web.Wasm.DTO +{ + public class EventDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public OrganizerDto Organizer { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public AddressDto Location { get; set; } + public IEnumerable MediaFilesUrl { get; set; } + public int InterestedStudents { get; set; } + public int SignedUpStudents { get; set; } + public int Capacity { get; set; } + public decimal Fee { get; set; } + public Category Category { get; set; } + public string Status { get; set; } + public DateTime PublishDate { get; set; } + public DateTime UpdatedAt { get; set; } + public bool IsSignedUp { get; set; } + public bool IsInterested { get; set; } + public int? StudentRating { get; set; } + public IEnumerable FriendsInterestedIn { get; set; } + public IEnumerable FriendsSignedUp { get; set; } + public string BannerUrl { get; set; } + public EventSettings Settings { get; set; } + public EventDto() { } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EventRatingDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EventRatingDto.cs new file mode 100644 index 000000000..d1ba37977 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/EventRatingDto.cs @@ -0,0 +1,11 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class EventRatingDto + { + public Guid EventId { get; set; } + public int TotalRatings { get; set; } + public double AverageRating { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Events/Category.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Events/Category.cs new file mode 100644 index 000000000..deef24c6f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Events/Category.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Events +{ + public enum Category + { + Music, + Sports, + Education, + Science, + Technology, + Art, + Business, + Health, + Charity, + Social, + Networking, + FoodAndDrink, + Travel, + Outdoors, + Fitness, + Entertainment, + Politics, + Religion, + Family, + History, + Literature, + Fashion, + Film, + Gaming, + Cultural, + Environmental, + PersonalDevelopment, + Finance, + Other + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FileDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FileDto.cs new file mode 100644 index 000000000..7cb83b5e8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FileDto.cs @@ -0,0 +1,17 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class FileDto + { + public Guid MediaFileId { get; set; } + public Guid SourceId { get; set; } + public string SourceType { get; set; } + public string State { get; set; } + public DateTime CreatedAt { get; set; } + public Guid UploaderId { get; set; } + public string FileName { get; set; } + public string FileContentType { get; set; } + public string Base64Content { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FileUploadResponseDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FileUploadResponseDto.cs new file mode 100644 index 000000000..67451fff8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FileUploadResponseDto.cs @@ -0,0 +1,11 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class FileUploadResponseDto + { + public Guid Id { get; set; } + public string OriginalUrl { get; set; } + public string ProcessedUrl { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/FriendDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/FriendDto.cs new file mode 100644 index 000000000..a268572ef --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/FriendDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.States; + +namespace Astravent.Web.Wasm.DTO.Friends +{ + public class FriendDto + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid FriendId { get; set; } + public DateTime CreatedAt { get; set; } + public FriendState State { get; set; } + public StudentDto StudentDetails { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/FriendRequestDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/FriendRequestDto.cs new file mode 100644 index 000000000..e56f9cfde --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/FriendRequestDto.cs @@ -0,0 +1,22 @@ +using System; +using Astravent.Web.Wasm.DTO.States; + +namespace Astravent.Web.Wasm.DTO.Friends +{ + public class FriendRequestDto + { + public Guid Id { get; set; } + public Guid InviterId { get; set; } + public Guid InviteeId { get; set; } + public DateTime RequestedAt { get; set; } + public FriendState State { get; set; } + public Guid UserId { get; set; } + + public string InviteeName { get; set; } + public string InviterName { get; set; } + public string InviteeEmail { get; set; } + public string InviteeImage { get; set; } + public string InviterEmail { get; set; } + public string InviterImage { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/UserFriendsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/UserFriendsDto.cs new file mode 100644 index 000000000..e5ab4beed --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/UserFriendsDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Friends +{ + public class UserFriendsDto + { + public Guid UserId { get; set; } + public List Friends { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/UserRequestsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/UserRequestsDto.cs new file mode 100644 index 000000000..abcf2a04a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Friends/UserRequestsDto.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Friends +{ + public class UserRequestsDto + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public List FriendRequests { get; set; } = new List(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FrontendVersion.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FrontendVersion.cs new file mode 100644 index 000000000..0bab38dd3 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/FrontendVersion.cs @@ -0,0 +1,11 @@ +namespace Astravent.Web.Wasm.DTO +{ + public enum FrontendVersion + { + Auto, + DarkMode, + LightMode, + SystemMode, + Default + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/GalleryImageDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/GalleryImageDto.cs new file mode 100644 index 000000000..50e67bb8c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/GalleryImageDto.cs @@ -0,0 +1,18 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class GalleryImageDto + { + public Guid ImageId { get; set; } + public string ImageUrl { get; set; } + public DateTime DateAdded { get; set; } + + public GalleryImageDto(Guid imageId, string imageUrl, DateTime dateAdded) + { + ImageId = imageId; + ImageUrl = !string.IsNullOrWhiteSpace(imageUrl) ? imageUrl : "/images/default_image.png"; + DateAdded = dateAdded; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/GeneralFileUploadResponseDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/GeneralFileUploadResponseDto.cs new file mode 100644 index 000000000..e1a1d90f8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/GeneralFileUploadResponseDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class GeneralFileUploadResponseDto + { + public Guid Id { get; set; } + public string FileUrl { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/InterestDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/InterestDto.cs new file mode 100644 index 000000000..447bdf046 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/InterestDto.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Astravent.Web.Wasm.DTO +{ + public class InterestDto + { + public string Name { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Interests/Interest.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Interests/Interest.cs new file mode 100644 index 000000000..90ece90f2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Interests/Interest.cs @@ -0,0 +1,482 @@ +namespace Astravent.Web.Wasm.DTO.Interests +{ + public enum Interest + { + Acting, + Airsoft, + Archery, + Astronomy, + Automotive, + Backpacking, + Badminton, + Baking, + Ballet, + Barbecue, + Basketball, + BeachVolleyball, + BirdWatching, + Blogging, + BoardGames, + BookClubs, + Bowling, + Boxing, + BrewingBeer, + BungeeJumping, + Calligraphy, + Camping, + Canoeing, + CardGames, + Chess, + Coding, + Collecting, + Comedy, + ComicBooks, + Cooking, + Crafting, + Cricket, + Crossfit, + Cycling, + Dancing, + Debate, + Darts, + DiningOut, + DIYProjects, + Drawing, + Embroidery, + Esports, + FantasySports, + Fashion, + FilmMaking, + Fishing, + Fitness, + FloralArranging, + FlyingDrones, + Football, + Gardening, + Genealogy, + Geocaching, + GlassBlowing, + Golf, + GourmetFood, + Gymnastics, + Hiking, + History, + Hockey, + HomeBrewing, + HorseRiding, + Hunting, + IceSkating, + Improv, + Investing, + JewelryMaking, + Juggling, + Kayaking, + Kitesurfing, + Knitting, + Lacrosse, + Landscaping, + LaserTag, + LegoBuilding, + Magic, + MartialArts, + Meditation, + MetalDetecting, + ModelBuilding, + MountainBiking, + Movies, + Music, + Networking, + Origami, + Painting, + Parkour, + PetCare, + Philately, + Philosophy, + Photography, + Piano, + Pilates, + PingPong, + Podcasts, + Poetry, + Pottery, + Puzzles, + Quilting, + Racing, + Rafting, + Reading, + RealEstate, + Robotics, + RockClimbing, + Rowing, + Rugby, + Running, + Sailing, + ScubaDiving, + Sewing, + Singing, + Skateboarding, + Skiing, + SkyDiving, + Snowboarding, + SoapMaking, + Soccer, + Spirituality, + StandUpComedy, + Surfing, + Swimming, + TableTennis, + Taekwondo, + TaiChi, + Tennis, + Theater, + Travel, + Trekking, + Trivia, + VideoGames, + Volleyball, + Volunteering, + Walking, + WatchingMovies, + WatchingSports, + WaterPolo, + Weaving, + WeightLifting, + WineTasting, + WoodWorking, + Writing, + Yoga, + Zumba, + ArcheryTag, + Astrophotography, + AutoRacing, + BadmintonSingles, + Beachcombing, + BirdPhotography, + BoardgameDesign, + Bodybuilding, + BookWriting, + BowlingTeams, + CampingHiking, + CanoeCamping, + CarShows, + CheeseMaking, + ChessTournaments, + ClaySculpting, + Climbing, + ComputerProgramming, + Concerts, + CraftBeer, + CreativeWriting, + Crochet, + DanceTeams, + DiningExperiences, + DIYHomeImprovement, + DollCollecting, + DroneRacing, + EnduranceSports, + EventPlanning, + Exercise, + FantasyFootball, + FineDining, + FloralDesign, + FoodTasting, + GardeningVegetables, + Gemology, + GhostHunting, + GlassArt, + GolfCourses, + GourmetCooking, + GymWorkouts, + Handball, + HistoricReenactments, + HomeRenovation, + HotAirBallooning, + IceFishing, + IndoorClimbing, + JewelryDesign, + JigsawPuzzles, + Judo, + KayakFishing, + KiteFlying, + KnittingClubs, + LeatherWorking, + LegoCompetitions, + Lighthouses, + MagicShows, + MakeupArtistry, + MartialArtsTournaments, + MeditationRetreats, + MiniatureBuilding, + ModelAircraft, + ModelRailroading, + MountainClimbing, + MovieProduction, + MusicBands, + NatureHiking, + OrigamiArt, + PaintingClasses, + Paragliding, + ParkPhotography, + PetShows, + Philanthropy, + PhilatelyClubs, + PhilosophyGroups, + PianoConcerts, + PilatesClasses, + Poker, + PoliticalDebates, + PotteryClasses, + Powerlifting, + PuzzleChallenges, + QuiltingGroups, + RacketSports, + RaftingTrips, + ReadingCircles, + RealEstateInvesting, + Reenactments, + RoboticsClubs, + RockCollecting, + RunningMarathons, + SailboatRacing, + ScavengerHunts, + SculptureArt, + SkateParks, + SkiMountaineering, + SkyPhotography, + SoapBoxRacing, + Softball, + SpeedSkating, + SpiritualRetreats, + SportsAnalytics, + SportsPhotography, + Storytelling, + SurfPhotography, + SurfFishing, + SurvivalSkills, + TableFootball, + TaiChiClasses, + TalentShows, + Technology, + TennisMatches, + TheaterProduction, + TheatricalMakeup, + TravelPhotography, + TreeClimbing, + TriviaNights, + TropicalFish, + UrbanExploring, + VideoProduction, + VirtualReality, + VolunteerFirefighting, + Wakeboarding, + WaterAerobics, + WaterSkiing, + WeightTraining, + WineMaking, + WoodCarving, + WordGames, + Wrestling, + WritingPoetry, + YogaClasses, + ZipLining, + Zookeeping, + AirRifle, + AnimalRescue, + Aquascaping, + ArtGalleries, + BakingClasses, + BallroomDancing, + BaristaSkills, + Beekeeping, + BikeRacing, + Blacksmithing, + BoatBuilding, + BookRestoration, + BonsaiGrowing, + BrewingCider, + BullRiding, + ButterflyWatching, + CalligraphyArt, + CandleMaking, + CardTricks, + CaveExploring, + CelticMusic, + ChainsawCarving, + CircusArts, + ClassicCars, + ClayPigeonShooting, + Clowning, + ComicCollecting, + CompetitiveEating, + Cosplaying, + CountryDancing, + CraftFairs, + CricketUmpiring, + CrossCountrySkiing, + Cupping, + CurlingTeams, + DanceFitness, + DanceHall, + Design, + DogTraining, + Dominoes, + DragonBoatRacing, + ElectricVehicles, + Electronics, + FireDancing, + FirePoi, + FleaMarkets, + FolkDancing, + FoodStalls, + Forestry, + GameDesign, + GemCutting, + GeocachingHikes, + GiantPuppets, + GlassEtching, + Golfing, + GourmetCatering, + GreenEnergy, + Guitar, + Hackathons, + HandmadeCards, + HerbGardening, + HighlandGames, + HistoricalTours, + HomeAutomation, + Horticulture, + IceDiving, + InlineHockey, + InstrumentMaking, + JewelryMakingClasses, + KettlebellTraining, + KiteSurfing, + LandArt, + LaserShows, + Leathercraft, + LightArt, + LiveActionRolePlaying, + Macrame, + MarathonRunning, + MarineBiology, + MetalArt, + Microscopy, + MineralCollecting, + MixedMartialArts, + MockTrials, + ModelShips, + Monologues, + MosaicArt, + MountainRunning, + MuseumTours, + MushroomHunting, + NatureConservation, + NeedleFelting, + Needlepoint, + NightPhotography, + OrienteeringSports, + OutdoorCooking, + Paddleboarding, + PaperMaking, + ParkourClasses, + PerfumeMaking, + PetTraining, + PhilosophyLectures, + PhotographyClasses, + PianoLessons, + Pinball, + PinballMachines, + PlayingDrums, + PlayingGuitar, + PlayingSaxophone, + PlayingViolin, + PoleDancing, + PotluckDinners, + Powerboating, + PrecisionShooting, + PublicSpeaking, + Puppetry, + Quilling, + RallyDriving, + RCBoats, + RCPlanes, + RCTrucks, + RockMusic, + Rockhounding, + Rocketry, + RollerHockey, + RowingClubs, + RubiksCube, + RunningClubs, + Sailboard, + SalsaDancing, + SandSculpting, + ScienceFairs, + ScrapMetalArt, + Scrying, + SecretSanta, + SewingClasses, + Shogi, + ShrubSculpting, + SkyWatching, + SlotCars, + SlowFood, + SoapSculpting, + SoftballLeagues, + SolarEnergy, + Spelunking, + SpokenWord, + SportsScience, + SquashMatches, + StainedGlass, + StandUpPaddleboarding, + Stargazing, + Steampunk, + StoneSkipping, + StreetDancing, + StreetPerforming, + SunPhotography, + SurfKayaking, + SwingDancing, + SwordFighting, + TableHockey, + TableSoccer, + TangoDancing, + TattooArt, + TeamSports, + TechnicalWriting, + TextileArt, + TheaterActing, + TheatreHistory, + ThrowingDarts, + TimeCapsules, + ToyCollecting, + TraditionalGames, + TrampoliningTeams, + Trainspotting, + Triathlons, + TroutFishing, + TruckRacing, + UrbanGardening, + VideoBlogging, + VintageCars, + Volcanology, + WakeSurfing, + WaterBalloonFight, + Watercolors, + WheelchairBasketball, + Whittling, + WildflowerPhotography, + WildlifePhotography, + Windsurfing, + Woodburning, + WordSearch, + WorldBuilding, + WritingFiction, + WritingNonfiction, + WritingWorkshops, + Yachting, + YogaRetreats, + ZumbaFitness + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/InvitationModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/InvitationModel.cs new file mode 100644 index 000000000..89d79fac2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/InvitationModel.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO +{ + public class InvitationModel + { + public List SelectedFriendIds { get; set; } = new List(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/JwtDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/JwtDto.cs new file mode 100644 index 000000000..3b8906350 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/JwtDto.cs @@ -0,0 +1,14 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class JwtDto + { + public string AccessToken { get; set; } + public string Role { get; set; } + public string RefreshToken { get; set; } + public long Expires { get; set; } + public bool IsTwoFactorRequired { get; set; } + public Guid UserId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Languages/Language.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Languages/Language.cs new file mode 100644 index 000000000..229850cf8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Languages/Language.cs @@ -0,0 +1,52 @@ +namespace Astravent.Web.Wasm.DTO.Languages +{ + public enum Language + { + English, + Spanish, + French, + German, + Chinese, + Japanese, + Korean, + Italian, + Russian, + Portuguese, + Arabic, + Hindi, + Bengali, + Punjabi, + Javanese, + Vietnamese, + Telugu, + Marathi, + Tamil, + Urdu, + Turkish, + Persian, + Gujarati, + Polish, + Ukrainian, + Dutch, + Greek, + Czech, + Swedish, + Hungarian, + Danish, + Finnish, + Norwegian, + Hebrew, + Malay, + Indonesian, + Thai, + Filipino, + Swahili, + Zulu, + Xhosa, + Yoruba, + Igbo, + Amharic, + Somali, + Hausa + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/NotificationPreferencesDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/NotificationPreferencesDto.cs new file mode 100644 index 000000000..2f1d9d318 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/NotificationPreferencesDto.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO +{ + public class NotificationPreferencesDto + { + public Guid UserId { get; set; } + public bool AccountChanges { get; set; } + public bool SystemLogin { get; set; } + public bool NewEvent { get; set; } + public bool InterestBasedEvents { get; set; } + public bool EventNotifications { get; set; } + public bool CommentsNotifications { get; set; } + public bool PostsNotifications { get; set; } + public bool FriendsNotifications { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationDto.cs new file mode 100644 index 000000000..a6f6225ef --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationDto.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Astravent.Web.Wasm.DTO.Enums; + +namespace Astravent.Web.Wasm.DTO.Notifications +{ + public class NotificationDto + { + public Guid NotificationId { get; set; } + public Guid UserId { get; set; } + public string Message { get; set; } + public string Status { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public Guid? RelatedEntityId { get; set; } + public NotificationEventType EventType { get; set; } + public string Details { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationLinkFactory.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationLinkFactory.cs new file mode 100644 index 000000000..3963d8f8f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationLinkFactory.cs @@ -0,0 +1,47 @@ +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Notifications; +using System; + +namespace Astravent.Web.Wasm.DTO.Notifications +{ + public static class NotificationLinkFactory + { + public static string GetNotificationLink(NotificationDto notification) + { + switch (notification.EventType) + { + case NotificationEventType.NewEvent: + case NotificationEventType.EventDeleted: + case NotificationEventType.EventNewSignUp: + case NotificationEventType.EventNewSignUpFriend: + case NotificationEventType.StudentCancelledSignedUpToEvent: + case NotificationEventType.StudentShowedInterestInEvent: + case NotificationEventType.StudentCancelledInterestInEvent: + case NotificationEventType.EventParticipantAdded: + case NotificationEventType.EventParticipantRemoved: + case NotificationEventType.EventReminder: + return $"/events/{notification.RelatedEntityId}"; + + case NotificationEventType.PostCreated: + case NotificationEventType.PostUpdated: + case NotificationEventType.MentionedInPost: + // return $"/events/{notification.ParentEntityId}/posts/{notification.RelatedEntityId}/author/{Uri.EscapeDataString(notification.AdditionalInfo)}"; + + case NotificationEventType.CommentCreated: + case NotificationEventType.CommentUpdated: + // return $"/events/{notification.ParentEntityId}/posts/{notification.GrandParentEntityId}/comments/{notification.RelatedEntityId}"; + + case NotificationEventType.ReactionAdded: + case NotificationEventType.ReportCreated: + case NotificationEventType.ReportDeleted: + case NotificationEventType.ReportRejected: + case NotificationEventType.ReportResolved: + case NotificationEventType.ReportReviewStarted: + // return $"/reports/{notification.RelatedEntityId}"; + + default: + return $"/user-details/{notification.RelatedEntityId}"; + } + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationToUsersDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationToUsersDto.cs new file mode 100644 index 000000000..ac9151c9a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Notifications/NotificationToUsersDto.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Notifications +{ + public class NotificationToUsersDto + { + public Guid NotificationId { get; set; } + public Guid UserId { get; set; } + public string Message { get; set; } + public IEnumerable StudentIds { get; set; } + public Guid EventId { get; set; } + public NotificationToUsersDto() { } + + public NotificationToUsersDto(Guid notificationId, Guid userId, string message, IEnumerable studentIds, Guid eventId) + { + NotificationId = notificationId; + UserId = userId; + Message = message; + StudentIds = studentIds; + EventId = eventId; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/InvitationDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/InvitationDto.cs new file mode 100644 index 000000000..9d3f82b7b --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/InvitationDto.cs @@ -0,0 +1,14 @@ +using System; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class InvitationDto + { + public Guid UserId { get; set; } + + public InvitationDto() + { + + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationDetailsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationDetailsDto.cs new file mode 100644 index 000000000..71fda787f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationDetailsDto.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class OrganizationDetailsDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + public Guid OwnerId { get; set; } + public Guid? ParentOrganizationId { get; set; } + public IEnumerable SubOrganizations { get; set; } = new List(); + public IEnumerable Invitations { get; set; } = new List(); + public IEnumerable Users { get; set; } = new List(); + public IEnumerable Roles { get; set; } = new List(); + public IEnumerable Gallery { get; set; } = new List(); + public OrganizationSettingsDto Settings { get; set; } + public string DefaultRoleName { get; set; } + + public string Address { get; set; } + public string Country { get; set; } + public string City { get; set; } + public string Telephone { get; set; } + public string Email { get; set; } + + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationDto.cs new file mode 100644 index 000000000..eeb2df763 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationDto.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class OrganizationDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + public Guid OwnerId { get; set; } + public Guid? RootId { get; set; } + public string DefaultRoleName { get; set; } + + public string Address { get; set; } + public string Country { get; set; } + public string City { get; set; } + public string Telephone { get; set; } + public string Email { get; set; } + + public IEnumerable Users { get; set; } = new List(); + public OrganizationSettingsDto Settings { get; set; } = new OrganizationSettingsDto(); + public int UserCount => Users?.Count() ?? 0; + + public bool IsExpanded { get; set; } = false; + public List SubOrganizations { get; set; } = new List(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationGalleryDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationGalleryDto.cs new file mode 100644 index 000000000..96310bce7 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationGalleryDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + [ExcludeFromCodeCoverage] + public class OrganizationGalleryDto + { + public OrganizationDto Organization { get; set; } + public IEnumerable Gallery { get; set; } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationGalleryUsersDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationGalleryUsersDto.cs new file mode 100644 index 000000000..2db8a4932 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationGalleryUsersDto.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class OrganizationGalleryUsersDto + { + public OrganizationDetailsDto OrganizationDetails { get; set; } + public IEnumerable Gallery { get; set; } = new List(); + public IEnumerable Users { get; set; } = new List(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationRequestDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationRequestDto.cs new file mode 100644 index 000000000..57531a07c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationRequestDto.cs @@ -0,0 +1,13 @@ +using System; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class OrganizationRequestDto + { + public Guid RequestId { get; set; } + public Guid UserId { get; set; } + public DateTime RequestDate { get; set; } + public string State { get; set; } + public string Reason { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationRequestsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationRequestsDto.cs new file mode 100644 index 000000000..9eb0314d9 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationRequestsDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class OrganizationRequestsDto + { + public Guid OrganizationId { get; set; } + public IEnumerable Requests { get; set; } + + public OrganizationRequestsDto() + { + Requests = new List(); + } + + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationSettingsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationSettingsDto.cs new file mode 100644 index 000000000..203731670 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationSettingsDto.cs @@ -0,0 +1,19 @@ +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class OrganizationSettingsDto + { + public bool IsVisible { get; set; } + public bool IsPublic { get; set; } + public bool IsPrivate { get; set; } + public bool CanAddComments { get; set; } + public bool CanAddReactions { get; set; } + public bool CanPostPosts { get; set; } + public bool CanPostEvents { get; set; } + public bool CanMakeReposts { get; set; } + public bool CanAddCommentsToPosts { get; set; } + public bool CanAddReactionsToPosts { get; set; } + public bool CanAddCommentsToEvents { get; set; } + public bool CanAddReactionsToEvents { get; set; } + public bool DisplayFeedInMainOrganization { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationUserDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationUserDto.cs new file mode 100644 index 000000000..440b8f382 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationUserDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class OrganizationUserDto + { + public Guid Id { get; set; } + public RoleDto Role { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationUsersDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationUsersDto.cs new file mode 100644 index 000000000..ae7a3d0c3 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/OrganizationUsersDto.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + + public class OrganizationUsersDto + { + public OrganizationDto Organization { get; set; } + public IEnumerable Users { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/RoleDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/RoleDto.cs new file mode 100644 index 000000000..38037be4f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/RoleDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Enums; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class RoleDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public Dictionary Permissions { get; set; } + + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/SubOrganizationDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/SubOrganizationDto.cs new file mode 100644 index 000000000..2eecd529a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/SubOrganizationDto.cs @@ -0,0 +1,14 @@ +using System; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class SubOrganizationDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + public Guid OwnerId { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/UserOrganizationsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/UserOrganizationsDto.cs new file mode 100644 index 000000000..1fa25b4a2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Organizations/UserOrganizationsDto.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Astravent.Web.Wasm.DTO.Organizations +{ + public class UserOrganizationsDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string BannerUrl { get; set; } + public string ImageUrl { get; set; } + public Guid OwnerId { get; set; } + public IEnumerable SubOrganizations { get; set; } + public bool HasSubOrganizations => SubOrganizations != null && SubOrganizations.Any(); + + public IEnumerable Users { get; set; } = new List(); + public int UserCount => Users?.Count() ?? 0; + + public IEnumerable AllChildrenIds { get; set; } = new List(); + public bool IsExpanded { get; set; } = false; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/OrganizerDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/OrganizerDto.cs new file mode 100644 index 000000000..2e6230ebc --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/OrganizerDto.cs @@ -0,0 +1,14 @@ +using System; +using Astravent.Web.Wasm.DTO.Enums; + +namespace Astravent.Web.Wasm.DTO +{ + public class OrganizerDto + { + public Guid Id { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public OrganizerType OrganizerType { get; set; } + + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/PaginatedResponseDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/PaginatedResponseDto.cs new file mode 100644 index 000000000..192c529f3 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/PaginatedResponseDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO +{ + public class PaginatedResponseDto + { + public List Results { get; set; } + public int Total { get; set; } + public int Page { get; set; } + public int PageSize { get; set; } + public string NextPage { get; set; } + public string PrevPage { get; set; } + } + +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ParticipantDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ParticipantDto.cs new file mode 100644 index 000000000..86fa35cd4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ParticipantDto.cs @@ -0,0 +1,9 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class ParticipantDto + { + public Guid StudentId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Posts/PostDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Posts/PostDto.cs new file mode 100644 index 000000000..758f83c7f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Posts/PostDto.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Posts +{ + public class PostDto + { + public Guid Id { get; set; } + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public string TextContent { get; set; } + public IEnumerable MediaFiles { get; set; } + public string State { get; set; } + public DateTime? PublishDate { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public string Context { get; set; } + public string Visibility { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Posts/PostType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Posts/PostType.cs new file mode 100644 index 000000000..8f96cd14b --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Posts/PostType.cs @@ -0,0 +1,8 @@ +namespace Astravent.Web.Wasm.DTO.Posts +{ + public enum PostType + { + BlogPost, + SocialPost + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/PreferredLanguage.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/PreferredLanguage.cs new file mode 100644 index 000000000..c4f9c3e4f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/PreferredLanguage.cs @@ -0,0 +1,16 @@ +namespace Astravent.Web.Wasm.DTO +{ + public enum PreferredLanguage + { + English, + Polish, + Ukrainian, + Spanish, + French, + German, + Chinese, + Japanese, + Russian, + Other + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReactionDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReactionDto.cs new file mode 100644 index 000000000..1ad72fc08 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReactionDto.cs @@ -0,0 +1,16 @@ +using System; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.Enums.Reactions; + +namespace Astravent.Web.Wasm.DTO +{ + public class ReactionDto + { + public Guid Id { get; set; } + public Guid UserId { get; set; } + public Guid ContentId { get; set; } + public ReactionContentType ContentType { get; set; } + public ReactionType ReactionType { get; set; } + public ReactionTargetType TargetType { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReactionsSummaryDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReactionsSummaryDto.cs new file mode 100644 index 000000000..e84e646bc --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReactionsSummaryDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Enums; + +namespace Astravent.Web.Wasm.DTO +{ + public class ReactionsSummaryDto + { + public int NumberOfReactions { get; set; } + public ReactionType? DominantReaction { get; set; } + public Guid? AuthUserReactionId { get; set; } + public ReactionType? AuthUserReactionType { get; set; } + public Dictionary ReactionsWithCounts { get; set; } = new Dictionary(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReportDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReportDto.cs new file mode 100644 index 000000000..76ad6b8fe --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ReportDto.cs @@ -0,0 +1,22 @@ +using System; +using Astravent.Web.Wasm.DTO.Enums; +using Astravent.Web.Wasm.DTO.States; +using Astravent.Web.Wasm.DTO.Types; + +namespace Astravent.Web.Wasm.DTO +{ + public class ReportDto + { + public Guid Id { get; set; } + public Guid IssuerId { get; set; } + public Guid TargetId { get; set; } + public Guid TargetOwnerId { get; set; } + public ReportContextType ContextType { get; set; } + public ReportCategory Category { get; set; } + public string Reason { get; set; } + public ReportState State { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + public Guid? ReviewerId { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ResetModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ResetModel.cs new file mode 100644 index 000000000..6986a4b4c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ResetModel.cs @@ -0,0 +1,7 @@ +namespace Astravent.Web.Wasm.DTO +{ + public class ResetModel + { + public string Email { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ResetPasswordModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ResetPasswordModel.cs new file mode 100644 index 000000000..53a6d46fc --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/ResetPasswordModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO +{ + public class ResetPasswordModel + { + public string Email { get; set; } + public string NewPassword { get; set; } + public string ConfirmPassword { get; set; } + public string Token { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/States/FriendState.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/States/FriendState.cs new file mode 100644 index 000000000..d6a53aabb --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/States/FriendState.cs @@ -0,0 +1,14 @@ +namespace Astravent.Web.Wasm.DTO.States +{ + public enum FriendState + { + Unknown, + Requested, + Accepted, + Declined, + Blocked, + Cancelled, + Confirmed, + Pending + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/States/ReportState.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/States/ReportState.cs new file mode 100644 index 000000000..ba1a291d0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/States/ReportState.cs @@ -0,0 +1,29 @@ +using System; + +namespace Astravent.Web.Wasm.DTO.States +{ + public enum ReportState + { + Submitted, + UnderReview, + Resolved, + Rejected, + Cancelled + } + + public static class ReportStateExtensions + { + public static string GetReportStateText(ReportState reportState) + { + return reportState switch + { + ReportState.Submitted => "Submitted", + ReportState.UnderReview => "Under review", + ReportState.Resolved => "Resolved", + ReportState.Rejected => "Rejected", + ReportState.Cancelled => "Cancelled", + _ => throw new ArgumentOutOfRangeException(nameof(reportState), reportState, null) + }; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/StudentDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/StudentDto.cs new file mode 100644 index 000000000..c9439710e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/StudentDto.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Languages; +using Astravent.Web.Wasm.DTO.Interests; + +namespace Astravent.Web.Wasm.DTO +{ + public class StudentDto + { + public Guid Id { get; set; } + public string Email { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string ProfileImageUrl { get; set; } + public string Description { get; set; } + public DateTime? DateOfBirth { get; set; } + public bool EmailNotifications { get; set; } + public bool IsBanned { get; set; } + public string State { get; set; } + public DateTime CreatedAt { get; set; } + public string ContactEmail { get; set; } + public string BannerUrl { get; set; } + public string PhoneNumber { get; set; } + public List Languages { get; set; } + public List Interests { get; set; } + public IEnumerable Education { get; set; } + public IEnumerable Work { get; set; } + public bool IsTwoFactorEnabled { get; set; } + public string TwoFactorSecret { get; set; } + public IEnumerable InterestedInEvents { get; set; } + public IEnumerable SignedUpEvents { get; set; } + public List GalleryOfImageUrls { get; set; } + public string Country { get; set; } + public string City { get; set; } + + public AvailableSettingsDto UserSettings { get; set; } + + public bool IsInvitationPending { get; set; } + public bool InvitationSent { get; set; } + public bool Selected { get; set; } + + public bool IsOnline { get; set; } + public string DeviceType { get; set; } + public DateTime? LastActive { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/StudentWithGalleryImagesDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/StudentWithGalleryImagesDto.cs new file mode 100644 index 000000000..2cdf9844b --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/StudentWithGalleryImagesDto.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Astravent.Web.Wasm.DTO +{ + public class StudentWithGalleryImagesDto + { + public StudentDto Student { get; set; } + public List GalleryImages { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Types/MediaFileContextType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Types/MediaFileContextType.cs new file mode 100644 index 000000000..43129a7e3 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Types/MediaFileContextType.cs @@ -0,0 +1,11 @@ +namespace Astravent.Web.Wasm.DTO.Types +{ + public enum MediaFileContextType + { + Event, + Post, + StudentProfileImage, + StudentBannerImage, + StudentGalleryImage + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Types/ReportContextType.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Types/ReportContextType.cs new file mode 100644 index 000000000..905560d1a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Types/ReportContextType.cs @@ -0,0 +1,27 @@ +using System; + +namespace Astravent.Web.Wasm.DTO.Types +{ + public enum ReportContextType + { + Event, + Post, + Comment, + StudentProfile + } + + public static class ReportContextTypeExtensions + { + public static string GetReportContextTypeText(ReportContextType reportContextType) + { + return reportContextType switch + { + ReportContextType.Event => "Event", + ReportContextType.Post => "Post", + ReportContextType.Comment => "Comment", + ReportContextType.StudentProfile => "Student Profile", + _ => throw new ArgumentOutOfRangeException(nameof(reportContextType), reportContextType, null) + }; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/UserDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/UserDto.cs new file mode 100644 index 000000000..c9d86c577 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/UserDto.cs @@ -0,0 +1,21 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class UserDto + { + public Guid Id { get; set; } + public string Name { get; set; } + public string Email { get; set; } + public string Role { get; set; } + public bool IsEmailVerified { get; set; } + public DateTime? EmailVerifiedAt { get; set; } + public bool IsTwoFactorEnabled { get; set; } + public string TwoFactorSecret { get; set; } + + public bool IsOnline { get; set; } + public string DeviceType { get; set; } + public DateTime? LastActive { get; set; } + public string IpAddress { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Users/BlockedUserDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Users/BlockedUserDto.cs new file mode 100644 index 000000000..8490c2714 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Users/BlockedUserDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Users +{ + public class BlockedUserDto + { + public Guid BlockerId { get; set; } + public Guid BlockedUserId { get; set; } + public DateTime BlockedAt { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Views/UserProfileViewDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Views/UserProfileViewDto.cs new file mode 100644 index 000000000..905093be4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Views/UserProfileViewDto.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.DTO.Views +{ + public class UserProfileViewDto + { + public Guid UserProfileId { get; set; } + public DateTime Date { get; set; } + public string IpAddress { get; set; } + public string DeviceType { get; set; } + public string OperatingSystem { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Visibility.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Visibility.cs new file mode 100644 index 000000000..c048c66ae --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Visibility.cs @@ -0,0 +1,9 @@ +namespace Astravent.Web.Wasm.DTO +{ + public enum Visibility + { + Everyone, + Connections, + NoOne + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/WorkDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/WorkDto.cs new file mode 100644 index 000000000..b44a8cde0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/WorkDto.cs @@ -0,0 +1,13 @@ +using System; + +namespace Astravent.Web.Wasm.DTO +{ + public class WorkDto + { + public string Company { get; set; } + public string Position { get; set; } + public DateTime? StartDate { get; set; } = null; + public DateTime? EndDate { get; set; } = null; + public string Description { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/EventParticipantsDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/EventParticipantsDto.cs new file mode 100644 index 000000000..519f562f0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/EventParticipantsDto.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Wrappers +{ + public class EventParticipantsDto + { + public Guid EventId { get; set; } + public IEnumerable InterestedStudents { get; set; } + public IEnumerable SignedUpStudents { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/PageableDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/PageableDto.cs new file mode 100644 index 000000000..e8aa0d570 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/PageableDto.cs @@ -0,0 +1,9 @@ +namespace Astravent.Web.Wasm.DTO.Wrappers +{ + public class PageableDto + { + public int Page { get; set; } + public int Size { get; set; } + public SortDto Sort { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/PagedResponseDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/PagedResponseDto.cs new file mode 100644 index 000000000..0d69c6345 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/PagedResponseDto.cs @@ -0,0 +1,20 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Wrappers +{ + public class PagedResponseDto : ResponseDto + { + public IEnumerable Items { get; set; } = new List(); + public int TotalPages { get; set; } + public int TotalItems { get; set; } + public int PageSize { get; set; } + public int Page { get; set; } + public bool First { get; set; } + public bool Last { get; set; } + public bool Empty { get; set; } + public int? NextPage => Page < TotalPages ? Page + 1 : (int?)null; + public int? PreviousPage => Page > 1 ? Page - 1 : (int?)null; + } + +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/ResponseDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/ResponseDto.cs new file mode 100644 index 000000000..fce615d1b --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/ResponseDto.cs @@ -0,0 +1,10 @@ +namespace Astravent.Web.Wasm.DTO.Wrappers +{ + public class ResponseDto + { + public T Content { get; set; } + public bool Succeeded { get; set; } + public string[] Errors { get; set; } + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/SortDto.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/SortDto.cs new file mode 100644 index 000000000..fad14d665 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/DTO/Wrappers/SortDto.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.DTO.Wrappers +{ + public class SortDto + { + public IEnumerable SortBy { get; set; } + public string Direction { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Comments/SearchRootComments.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Comments/SearchRootComments.cs new file mode 100644 index 000000000..610c251b8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Comments/SearchRootComments.cs @@ -0,0 +1,19 @@ +using System; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Data.Comments +{ + public class SearchRootComments + { + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + public PageableDto Pageable { get; set; } + + public SearchRootComments(Guid contextId, string commentContext, PageableDto pageable) + { + ContextId = contextId; + CommentContext = commentContext; + Pageable = pageable; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Comments/SearchSubComments.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Comments/SearchSubComments.cs new file mode 100644 index 000000000..48335f31e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Comments/SearchSubComments.cs @@ -0,0 +1,21 @@ +using System; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Data.Comments +{ + public class SearchSubComments + { + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + public Guid ParentId { get; set; } + public PageableDto Pageable { get; set; } + + public SearchSubComments(Guid contextId, string commentContext, Guid parentId, PageableDto pageable) + { + ContextId = contextId; + CommentContext = commentContext; + ParentId = parentId; + Pageable = pageable; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Events/SearchEvents.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Events/SearchEvents.cs new file mode 100644 index 000000000..4cb23d6a0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Events/SearchEvents.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Data.Events +{ + public class SearchEvents + { + public string Name { get; set; } + public string Organizer { get; set; } + public Guid OrganizationId { get; set; } + public Guid RootOrganizationId { get; set; } + public string Category { get; set; } + public string State { get; set; } + public IEnumerable Friends { get; set; } + public string FriendsEngagementType { get; set; } + public string DateFrom { get; set; } + public string DateTo { get; set; } + public PageableDto Pageable { get; set; } + + public SearchEvents(string name, string organizer, Guid organizationId, Guid rootOrganizationId, + string category, string state, IEnumerable friends, string friendsEngagementType, + string dateFrom, string dateTo, PageableDto pageable) + { + Name = name; + Organizer = organizer; + OrganizationId = organizationId; + RootOrganizationId = rootOrganizationId; + Category = category; + State = state; + Friends = friends; + FriendsEngagementType = friendsEngagementType; + DateFrom = dateFrom; + DateTo = dateTo; + Pageable = pageable; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Events/SearchOrganizerEvents.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Events/SearchOrganizerEvents.cs new file mode 100644 index 000000000..f5151ebc9 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Events/SearchOrganizerEvents.cs @@ -0,0 +1,26 @@ +using System; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Data.Events +{ + public class SearchOrganizerEvents + { + public string Name { get; set; } + public Guid OrganizerId { get; set; } + public string DateFrom { get; set; } + public string DateTo { get; set; } + public string State { get; set; } + public PageableDto Pageable { get; set; } + + public SearchOrganizerEvents(string name, Guid organizerId, string dateFrom, string dateTo, + string state, PageableDto pageable) + { + Name = name; + OrganizerId = organizerId; + DateFrom = dateFrom; + DateTo = dateTo; + State = state; + Pageable = pageable; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Posts/SearchPosts.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Posts/SearchPosts.cs new file mode 100644 index 000000000..13829427b --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Posts/SearchPosts.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Data.Posts +{ + public class SearchPosts + { + public Guid? UserId { get; set; } + public Guid? OrganizationId { get; set; } + public Guid? EventId { get; set; } + public PageableDto Pageable { get; set; } + + public SearchPosts() + { + Pageable = new PageableDto + { + Page = 1, + Size = 10, + Sort = new SortDto + { + SortBy = new List { "PublishDate" }, + Direction = "asc" + } + }; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Reports/SearchReports.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Reports/SearchReports.cs new file mode 100644 index 000000000..5fbb6d935 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Data/Reports/SearchReports.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Data.Reports +{ + public class SearchReports + { + public IEnumerable ContextTypes { get; set; } + public IEnumerable States { get; set; } + public Guid ReviewerId { get; set; } + public PageableDto Pageable { get; set; } + + public SearchReports(IEnumerable contextTypes, IEnumerable states, Guid reviewerId, + PageableDto pageable) + { + ContextTypes = contextTypes; + States = states; + ReviewerId = reviewerId; + Pageable = pageable; + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/CustomHttpClient.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/CustomHttpClient.cs new file mode 100644 index 000000000..2a88fba5f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/CustomHttpClient.cs @@ -0,0 +1,164 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Polly; +using Astravent.Web.Wasm.Areas.Friends; + +namespace Astravent.Web.Wasm.HttpClients +{ + public class CustomHttpClient : IHttpClient + { + private static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + DateFormatHandling = DateFormatHandling.IsoDateFormat, + DateTimeZoneHandling = DateTimeZoneHandling.Utc + + }; + + private readonly HttpClient _client; + private readonly HttpClientOptions _options; + private readonly ILogger _logger; + + public CustomHttpClient(HttpClient client, HttpClientOptions options, ILogger logger) + { + _client = client; + _options = options; + _logger = logger; + _client.BaseAddress = new Uri(options.ApiUrl); + } + + public void SetAccessToken(string token) + { + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + + public async Task GetAsync(string uri) + { + var (success, content) = await TryExecuteAsync(uri, client => client.GetAsync(uri)); + + if (!success) + return default; + + return JsonConvert.DeserializeObject(content, JsonSerializerSettings); + } + + public Task PostAsync(string uri, T request) + { + var jsonPayload = JsonConvert.SerializeObject(request, JsonSerializerSettings); + _logger.LogDebug($"Sending HTTP POST request to URI: {uri} with payload: {jsonPayload}"); + return TryExecuteAsync(uri, client => client.PostAsync(uri, GetPayload(request))); + } + + public async Task> PostAsync(string uri, TRequest request) + { + var jsonPayload = JsonConvert.SerializeObject(request, JsonSerializerSettings); + _logger.LogDebug($"Sending HTTP POST request to URI: {uri} with payload: {jsonPayload}"); + + var (success, content) = await TryExecuteAsync(uri, client => client.PostAsync(uri, GetPayload(request))); + + if (!success) + { + var errorMessage = JsonConvert.DeserializeObject(content, JsonSerializerSettings); + return new HttpResponse(errorMessage); + } + + var result = JsonConvert.DeserializeObject(content, JsonSerializerSettings); + return new HttpResponse(result); + } + + public Task PutAsync(string uri, T request) + => TryExecuteAsync(uri, client => client.PutAsync(uri, GetPayload(request))); + + public async Task> PutAsync(string uri, TRequest request) + { + var (success, content) = await TryExecuteAsync(uri, client => client.PutAsync(uri, GetPayload(request))); + + if (!success) + { + var errorMessage = JsonConvert.DeserializeObject(content, JsonSerializerSettings); + return new HttpResponse(errorMessage); + } + + var result = JsonConvert.DeserializeObject(content, JsonSerializerSettings); + return new HttpResponse(result); + } + + public Task DeleteAsync(string uri) + => TryExecuteAsync(uri, client => client.DeleteAsync(uri)); + + public async Task DeleteAsync(string uri, object payload) + { + var jsonPayload = JsonConvert.SerializeObject(payload, JsonSerializerSettings); + _logger.LogInformation($"Sending HTTP DELETE request to URI: {uri} with payload: {jsonPayload}"); + + var request = new HttpRequestMessage(HttpMethod.Delete, uri) + { + Content = new StringContent(jsonPayload, Encoding.UTF8, "text/plain") + }; + + var response = await _client.SendAsync(request); + if (!response.IsSuccessStatusCode) + { + var errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogInformation($"Error response from server: {errorContent}"); + throw new HttpRequestException($"Request to {uri} failed with status code {response.StatusCode} and message {errorContent}"); + } + } + + private static StringContent GetPayload(T request) + { + var json = JsonConvert.SerializeObject(request, JsonSerializerSettings); + // Set content type with charset parameter + return new StringContent(json, Encoding.UTF8, "text/plain"); + } + + private Task<(bool success, string content)> TryExecuteAsync(string uri, Func> client) + => Policy.Handle() + .WaitAndRetryAsync(_options.Retries, r => TimeSpan.FromSeconds(Math.Pow(2, r))) + .ExecuteAsync(async () => + { + if (_client.BaseAddress != null && !Uri.IsWellFormedUriString(uri, UriKind.Absolute)) + { + if (!uri.StartsWith("/")) uri = "/" + uri; + + uri = new Uri(_client.BaseAddress, uri).ToString(); + } + else if (!Uri.IsWellFormedUriString(uri, UriKind.Absolute)) + { + _logger.LogError($"The provided URI '{uri}' is not a valid absolute URL and no BaseAddress is set."); + return default; + } + + _logger.LogDebug($"Sending HTTP request to URI: {uri}"); + using (var response = await client(_client)) + { + if (response.IsSuccessStatusCode) + { + _logger.LogDebug($"Received a valid response to HTTP request from URI: {uri}" + + $"{Environment.NewLine}{response}"); + + return (true, await response.Content.ReadAsStringAsync()); + } + + var errorContent = "invalid_http_response"; + if (!response.IsSuccessStatusCode) + { + errorContent = await response.Content.ReadAsStringAsync(); + _logger.LogError($"Error response from server: {errorContent}"); + } + + _logger.LogError($"Received an invalid response to HTTP request from URI: {uri}" + + $"{Environment.NewLine}{response}"); + + return (false, errorContent); + } + }); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/ErrorMessage.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/ErrorMessage.cs new file mode 100644 index 000000000..8f059d101 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/ErrorMessage.cs @@ -0,0 +1,8 @@ +namespace Astravent.Web.Wasm.HttpClients +{ + public class ErrorMessage + { + public string Code { get; set; } + public string Reason { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/HttpClientOptions.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/HttpClientOptions.cs new file mode 100644 index 000000000..e4337c022 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/HttpClientOptions.cs @@ -0,0 +1,8 @@ +namespace Astravent.Web.Wasm.HttpClients +{ + public class HttpClientOptions + { + public string ApiUrl { get; set; } + public int Retries { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/HttpResponse.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/HttpResponse.cs new file mode 100644 index 000000000..ef9ae2580 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/HttpResponse.cs @@ -0,0 +1,22 @@ +namespace Astravent.Web.Wasm.HttpClients +{ + public class HttpResponse + { + public T Content { get; set; } + public HttpResponse() { } + public ErrorMessage ErrorMessage { get; set; } + public bool IsSuccessStatusCode { get; set; } + + public HttpResponse(T content) + { + Content = content; + IsSuccessStatusCode = true; + } + + public HttpResponse(ErrorMessage errorMessage) + { + ErrorMessage = errorMessage; + IsSuccessStatusCode = false; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/IHttpClient.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/IHttpClient.cs new file mode 100644 index 000000000..0ee3467d1 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/HttpClients/IHttpClient.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Astravent.Web.Wasm.Areas.Friends; + +namespace Astravent.Web.Wasm.HttpClients +{ + public interface IHttpClient + { + void SetAccessToken(string accessToken); + Task GetAsync(string uri); + Task PostAsync(string uri, T request); + Task> PostAsync(string uri, TRequest request); + Task PutAsync(string uri, T request); + Task> PutAsync(string uri, TRequest request); + Task DeleteAsync(string uri); + Task DeleteAsync(string uri, object payload); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/BlockedUsers/BlockedUserViewModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/BlockedUsers/BlockedUserViewModel.cs new file mode 100644 index 000000000..f2fcfff9e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/BlockedUsers/BlockedUserViewModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Models.BlockedUsers +{ + public class BlockedUserViewModel + { + public Guid BlockedUserId { get; set; } + public string FullName { get; set; } + public string ProfileImageUrl { get; set; } + public DateTime BlockedAt { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/CreateCommentModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/CreateCommentModel.cs new file mode 100644 index 000000000..ef22a6c53 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/CreateCommentModel.cs @@ -0,0 +1,15 @@ +using System; + +namespace Astravent.Web.Wasm.Models.Comments +{ + public class CreateCommentModel + { + public Guid CommentId { get; set; } + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + public Guid StudentId { get; set; } + public Guid ParentId { get; set; } + public string Comment { get; set; } + public bool CreatingSubmitted { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/DeleteCommentModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/DeleteCommentModel.cs new file mode 100644 index 000000000..6b2400a6a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/DeleteCommentModel.cs @@ -0,0 +1,10 @@ +using System; + +namespace Astravent.Web.Wasm.Models.Comments +{ + public class DeleteCommentModel + { + public Guid CommentId { get; set; } + public bool DeletingSubmitted { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/SearchCommentsModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/SearchCommentsModel.cs new file mode 100644 index 000000000..351a30c6d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/SearchCommentsModel.cs @@ -0,0 +1,14 @@ +using System; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Models.Comments +{ + public class SearchCommentsModel + { + public Guid ContextId { get; set; } + public string CommentContext { get; set; } + public Guid ParentId { get; set; } + public PageableDto Pageable { get; set; } + public bool SearchingSubmitted { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/UpdateCommentModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/UpdateCommentModel.cs new file mode 100644 index 000000000..e437190e2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Comments/UpdateCommentModel.cs @@ -0,0 +1,11 @@ +using System; + +namespace Astravent.Web.Wasm.Models.Comments +{ + public class UpdateCommentModel + { + public Guid CommentId { get; set; } + public string TextContent { get; set; } + public bool UpdatingSubmitted { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/CreateEventModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/CreateEventModel.cs new file mode 100644 index 000000000..a083b35b9 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/CreateEventModel.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO; +using Astravent.Web.Wasm.DTO.Organizations; + +namespace Astravent.Web.Wasm.Models.Events +{ + public class CreateEventModel + { + public Guid EventId { get; set; } + public string Name { get; set; } + public Guid OrganizerId { get; set; } + public OrganizationDto Organization { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public string BuildingName { get; set; } + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public IEnumerable MediaFiles { get; } + public string Description { get; set; } + public int Capacity { get; set; } + public decimal Fee { get; set; } + public string Category { get; set; } + public DateTime PublishDate { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/SearchEventsModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/SearchEventsModel.cs new file mode 100644 index 000000000..424331ce6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/SearchEventsModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Wrappers; +using Astravent.Web.Wasm.Models.Organizations; + +namespace Astravent.Web.Wasm.Models.Events +{ + public class SearchEventsModel + { + public string Name { get; set; } + public string Organizer { get; set; } + public OrganizationModel Organization { get; set; } + public string Category { get; set; } + public string State { get; set; } + public HashSet Friends { get; set; } + public string FriendsEngagementType { get; set; } + public DateTime DateFrom { get; set; } + public DateTime DateTo { get; set; } + public PageableDto Pageable { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/SearchOrganizerEventsModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/SearchOrganizerEventsModel.cs new file mode 100644 index 000000000..d0a2842c0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/SearchOrganizerEventsModel.cs @@ -0,0 +1,15 @@ +using System; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Models.Events +{ + public class SearchOrganizerEventsModel + { + public Guid OrganizerId { get; set; } + public string Name { get; set; } + public string State { get; set; } + public DateTime DateFrom { get; set; } + public DateTime DateTo { get; set; } + public PageableDto Pageable { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/UpdateEventModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/UpdateEventModel.cs new file mode 100644 index 000000000..d97af30b2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Events/UpdateEventModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.Models.Events +{ + public class UpdateEventModel + { + public Guid EventId { get; set; } + public string Name { get; set; } + public Guid OrganizerId { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public string BuildingName { get; set; } + public string Street { get; set; } + public string BuildingNumber { get; set; } + public string ApartmentNumber { get; set; } + public string City { get; set; } + public string ZipCode { get; set; } + public IEnumerable MediaFiles { get; set; } + public string Description { get; set; } + public int Capacity { get; set; } + public decimal Fee { get; set; } + public string Category { get; set; } + public DateTime PublishDate { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/SignInModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/SignInModel.cs new file mode 100644 index 000000000..800cb7a69 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/SignInModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Models.Identity +{ + public class SignInModel + { + public string Email { get; set; } + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/SignUpModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/SignUpModel.cs new file mode 100644 index 000000000..b75139d39 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/SignUpModel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Models.Identity +{ + public class SignUpModel + { + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + public string Password { get; set; } + public string ConfirmPassword { get; set; } + public string Role { get; set; } = "user"; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/TwoFactorModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/TwoFactorModel.cs new file mode 100644 index 000000000..3d86f27ef --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Identity/TwoFactorModel.cs @@ -0,0 +1,7 @@ +namespace Astravent.Web.Wasm.Models.Identity +{ + public class TwoFactorModel + { + public string Code { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Organizations/OrganizationModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Organizations/OrganizationModel.cs new file mode 100644 index 000000000..646b29ea1 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Organizations/OrganizationModel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.Models.Organizations +{ + public class OrganizationModel + { + public Guid Id { get; set; } + public string Name { get; set; } + public Guid RootId { get; set; } + public OrganizationModel Parent { get; set; } + public List Children { get; set; } + public bool Expanded { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Organizations/OrganizerModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Organizations/OrganizerModel.cs new file mode 100644 index 000000000..bb8c5975c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Organizations/OrganizerModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.Models.Organizations +{ + public class OrganizerModel + { + public Guid Id { get; set; } + public string Email { get; set; } + public string Name { get; set; } + public string Label { get; set; } + public bool WasBelonging { get; set; } + + public OrganizerModel(Guid id, string email, string name) + { + Id = id; + Email = email; + Name = name; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Posts/CreatePostModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Posts/CreatePostModel.cs new file mode 100644 index 000000000..2ba197ec2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Posts/CreatePostModel.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.Models.Posts +{ + public class CreatePostModel + { + public Guid PostId { get; set; } + public Guid EventId { get; set; } + public Guid OrganizerId { get; set; } + public string TextContent { get; set; } + public IEnumerable MediaFiles { get; set; } + public string State { get; set; } + public DateTime PublishDate { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Posts/UpdatePostModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Posts/UpdatePostModel.cs new file mode 100644 index 000000000..c2226590e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Posts/UpdatePostModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Astravent.Web.Wasm.Models.Posts +{ + public class UpdatePostModel + { + public Guid PostId { get; set; } + public string TextContent { get; set; } + public IEnumerable MediaFiles { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/CreateReportModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/CreateReportModel.cs new file mode 100644 index 000000000..d260c3c88 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/CreateReportModel.cs @@ -0,0 +1,15 @@ +using System; + +namespace Astravent.Web.Wasm.Models.Reports +{ + public class CreateReportModel + { + public Guid ReportId { get; set; } + public Guid IssuerId { get; set; } + public Guid TargetId { get; set; } + public Guid TargetOwnerId { get; set; } + public string ContextType { get; set; } + public string Category { get; set; } + public string Reason { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/GetStudentReportsModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/GetStudentReportsModel.cs new file mode 100644 index 000000000..cae28e10c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/GetStudentReportsModel.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Models.Reports +{ + public class GetStudentReportsModel + { + public int Page { get; set; } + public int Results { get; set; } + + public GetStudentReportsModel() + { + SetDefaultValues(); + } + + public void SetDefaultValues() + { + Page = 1; + Results = 5; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/SearchReportsModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/SearchReportsModel.cs new file mode 100644 index 000000000..c9cf79c93 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Reports/SearchReportsModel.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using Astravent.Web.Wasm.DTO.Wrappers; + +namespace Astravent.Web.Wasm.Models.Reports +{ + public class SearchReportsModel + { + public IEnumerable ContextTypes { get; set; } + public IEnumerable States { get; set; } + public bool OnlyReviewedByYou { get; set; } + public PageableDto Pageable { get; set; } + + public SearchReportsModel() + { + SetDefaultValues(); + } + + public void SetDefaultValues() + { + ContextTypes = ["Event", "Post", "Comment", "StudentProfile"]; + States = ["Submitted", "UnderReview", "Resolved", "Rejected", "Cancelled"]; + OnlyReviewedByYou = false; + Pageable = new PageableDto() + { + Page = 1, + Size = 5, + Sort = new SortDto() + { + SortBy = new List() { "updatedAt", "createdAt" }, + Direction = "des" + } + }; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Students/CompleteRegistrationModel.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Students/CompleteRegistrationModel.cs new file mode 100644 index 000000000..a32dad024 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Models/Students/CompleteRegistrationModel.cs @@ -0,0 +1,13 @@ +using System; + +namespace Astravent.Web.Wasm.Models.Students +{ + public class CompleteRegistrationModel + { + public Guid StudentId { get; set; } + public string ProfileImageUrl { get; set; } + public string Description { get; set; } + public DateTime DateOfBirth { get; set; } + public bool EmailNotifications { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/AboutApp.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/AboutApp.razor new file mode 100644 index 000000000..40892d4fe --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/AboutApp.razor @@ -0,0 +1,63 @@ +@using MudBlazor + +
+ +
+
+ + About MiniSpace + + + MiniSpace is a dynamic social platform designed to connect people and communities. Discover, share, and interact with content that drives innovation and engagement. + +
+
+
+ + + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/BlockedListComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/BlockedListComponent.razor new file mode 100644 index 000000000..d162c4056 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/BlockedListComponent.razor @@ -0,0 +1,145 @@ +@page "/blocked-users" +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject ISnackbar Snackbar +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Users +@using Astravent.Web.Wasm.Models.BlockedUsers +@using MudBlazor +@using System.Collections.Generic +@using System.Threading.Tasks + + + + Blocked Users + @if (isLoading) + { + + } + else + { + @if (blockedUsers?.Any() == true) + { + @foreach (var user in blockedUsers) + { + + + + + + + + + + + @user.FullName + Blocked on: @user.BlockedAt.ToString("MMMM dd, yyyy") + + + + + +
+ + Unblock + +
+
+
+
+
+ } + } + else + { + You have no blocked users. + } + } +
+ + + +@code { + private List blockedUsers = new(); + private bool isLoading = true; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + await LoadBlockedUsersAsync(); + isLoading = false; + } + + private async Task LoadBlockedUsersAsync() + { + try + { + var currentUserId = IdentityService.GetCurrentUserId(); + var response = await StudentsService.GetBlockedUsersAsync(currentUserId, 1, 10); + var blockedUserIds = response.Items.Select(bu => bu.BlockedUserId).ToList(); + + foreach (var blockedUserId in blockedUserIds) + { + var student = await StudentsService.GetStudentAsync(blockedUserId); + if (student != null) + { + blockedUsers.Add(new BlockedUserViewModel + { + BlockedUserId = blockedUserId, + FullName = $"{student.FirstName} {student.LastName}", + ProfileImageUrl = GetProfileImageUrl(student.ProfileImageUrl), + BlockedAt = response.Items.First(bu => bu.BlockedUserId == blockedUserId).BlockedAt + }); + } + } + } + catch (Exception ex) + { + Snackbar.Add($"Error loading blocked users: {ex.Message}", Severity.Error); + } + } + + private async Task UnblockUser(Guid blockedUserId) + { + try + { + var currentUserId = IdentityService.GetCurrentUserId(); + await StudentsService.UnblockUserAsync(currentUserId, blockedUserId); + blockedUsers.RemoveAll(u => u.BlockedUserId == blockedUserId); + Snackbar.Add("User has been unblocked successfully.", Severity.Success); + StateHasChanged(); + } + catch (Exception ex) + { + Snackbar.Add($"Error unblocking user: {ex.Message}", Severity.Error); + } + } + + private string GetProfileImageUrl(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "images/default_profile_image.webp" : profileImageUrl; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/EmailVerification.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/EmailVerification.razor new file mode 100644 index 000000000..37ea3af88 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/EmailVerification.razor @@ -0,0 +1,141 @@ +@page "/verify-email/{Token}/{Email}/{HashedToken}/verify" +@using Astravent.Web.Wasm.Areas.Identity +@using MudBlazor +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@using System.Web + + + +
+
+ +
+
+
+

Email Verification

+ @if (showSuccess) + { + + @successMessage + +
+ @* Include the sign-in form *@ + +
+ } + @if (showError) + { + + @errorMessage + + } +
+
+
+ +@code { + [Parameter] public string Token { get; set; } = ""; + [Parameter] public string Email { get; set; } = ""; + [Parameter] public string HashedToken { get; set; } = ""; + + private bool showSuccess = false; + private bool showError = false; + private string successMessage = string.Empty; + private string errorMessage = string.Empty; + + protected override async Task OnInitializedAsync() + { + Console.WriteLine($"Token received: {Token}"); + Console.WriteLine($"Email received: {Email}"); + Console.WriteLine($"HashedToken received: {HashedToken}"); + + if (!string.IsNullOrEmpty(Token) && !string.IsNullOrEmpty(Email) && !string.IsNullOrEmpty(HashedToken)) + { + var decodedEmail = HttpUtility.UrlDecode(Email); + var response = await IdentityService.VerifyEmailAsync(Token, decodedEmail, HashedToken); + if (response != null) + { + successMessage = "Thank you, your email has been verified."; + showSuccess = true; + } + else + { + errorMessage = "Sorry, we could not verify your email."; + showError = true; + } + } + else + { + errorMessage = "Invalid verification link."; + showError = true; + } + } + + private void OnAlertClose() + { + showSuccess = false; + showError = false; + successMessage = string.Empty; + errorMessage = string.Empty; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/EmailVerificationInfo.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/EmailVerificationInfo.razor new file mode 100644 index 000000000..a3d23efb5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/EmailVerificationInfo.razor @@ -0,0 +1,81 @@ +@page "/email-verification-info" +@using MudBlazor + + + + +
+
+ +
+
+
+ +
+
+
diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ForgotPassword.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ForgotPassword.razor new file mode 100644 index 000000000..860b6f144 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ForgotPassword.razor @@ -0,0 +1,130 @@ +@page "/forgot-password" +@using Astravent.Web.Wasm.Models.Identity +@using Astravent.Web.Wasm.Areas.Identity +@using Astravent.Web.Wasm.Areas.Http +@using Astravent.Web.Wasm.DTO +@using MudBlazor +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime + + + + +
+
+ +
+
+
+ + @if (showError) + { + + @errorMessage + + } + + + + + + + Send Reset Link + + + +
+ Return to Sign In +
+
+
+
+ +@code { + private ResetModel resetModel = new ResetModel(); + private bool showError = false; + private string errorMessage = string.Empty; + + private void OnAlertClose() + { + showError = false; + errorMessage = string.Empty; + } + + private async Task HandleResetPassword() + { + try + { + await IdentityService.ForgotPasswordAsync(resetModel.Email); + NavigationManager.NavigateTo("/reset-password-confirm"); + } + catch (Exception ex) + { + showError = true; + errorMessage = $"Error sending reset link: {ex.Message}"; + StateHasChanged(); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/GalleryComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/GalleryComponent.razor new file mode 100644 index 000000000..5784785c8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/GalleryComponent.razor @@ -0,0 +1,242 @@ +@page "/gallery" +@inject IIdentityService IdentityService +@inject IMediaFilesService MediaFilesService +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.Utilities +@using MudBlazor +@using System.IO + +Gallery +@if (IsLoading) +{ + +} +else +{ + +
+ + Upload New Image + +
+ + @if (StudentWithGalleryImagesDto.GalleryImages == null || !StudentWithGalleryImagesDto.GalleryImages.Any()) + { + No images found in the gallery. + } + else + { + + + @foreach (var image in StudentWithGalleryImagesDto.GalleryImages) + { + + + + } + + } +} + + + + + +@if (!string.IsNullOrEmpty(CroppedImageBase64)) +{ +
+ Cropped Image Preview: + +
+} + + +@code { + [Parameter] public bool IsLoading { get; set; } + [Parameter] public StudentWithGalleryImagesDto StudentWithGalleryImagesDto { get; set; } + [Parameter] public EventCallback SaveImageAsync { get; set; } + [Parameter] public bool IsUploading { get; set; } + + private string CroppedImageBase64 { get; set; } + private IBrowserFile croppedImageFile; + private Guid currentImageId; + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + try + { + // Load student's gallery images here (if not already loaded in the parent component) + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + StudentWithGalleryImagesDto.GalleryImages = null; + } + finally + { + IsLoading = false; + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", DotNetObjectReference.Create(this)); + } + } + + private async Task OpenCropper(InputFileChangeEventArgs e, Guid imageId) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageId = imageId; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, "profile"); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); + + try + { + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + var response = await MediaFilesService.UploadMediaFileAsync( + currentImageId, + "StudentGalleryImage", + IdentityService.GetCurrentUserId(), + $"gallery_image_{currentImageId}.png", + croppedImageFile.ContentType, + fileData); + + if (response.IsSuccessStatusCode) + { + // Refresh gallery images after successful upload + } + + StateHasChanged(); + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + CloseCropper(); + } + } + + private async Task RemoveImage(Guid imageId) + { + IsUploading = true; + StateHasChanged(); + + try + { + var imageUrl = StudentWithGalleryImagesDto.GalleryImages.FirstOrDefault(img => img.ImageId == imageId)?.ImageUrl; + if (!string.IsNullOrEmpty(imageUrl)) + { + await MediaFilesService.DeleteMediaFileAsync(imageUrl); + StudentWithGalleryImagesDto.GalleryImages.RemoveAll(img => img.ImageId == imageId); + await SaveImageAsync.InvokeAsync(null); + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred while removing the image: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/NotificationsComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/NotificationsComponent.razor new file mode 100644 index 000000000..0c38adf88 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/NotificationsComponent.razor @@ -0,0 +1,75 @@ +@page "/notifications-preferences" +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO +@using MudBlazor +@using System.Text.Json + +Notifications +@if (IsLoading) +{ + +} +else +{ + + + @if (StudentWithGalleryImagesDto.Student.EmailNotifications) + { + + + + + + + + + } + + Save preferences +} + +@code { + [Parameter] + public bool IsLoading { get; set; } + + [Parameter] + public NotificationPreferencesDto NotificationPreferencesDto { get; set; } + + [Parameter] + public StudentWithGalleryImagesDto StudentWithGalleryImagesDto { get; set; } + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + try + { + var studentId = IdentityService.GetCurrentUserId(); + StudentWithGalleryImagesDto = await StudentsService.GetStudentWithGalleryImagesAsync(studentId); + NotificationPreferencesDto = await StudentsService.GetUserNotificationPreferencesAsync(studentId); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + IsLoading = false; + } + } + + private async Task SaveNotificationPreferencesAsync() + { + try + { + var studentId = IdentityService.GetCurrentUserId(); + await StudentsService.UpdateUserNotificationPreferencesAsync(studentId, NotificationPreferencesDto, StudentWithGalleryImagesDto.Student.EmailNotifications); + Console.WriteLine("Notification preferences updated successfully."); + } + catch (Exception ex) + { + Console.WriteLine($"Error saving notification preferences: {ex.Message}"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/PrivacyComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/PrivacyComponent.razor new file mode 100644 index 000000000..da961d497 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/PrivacyComponent.razor @@ -0,0 +1,31 @@ +@page "/privacy" +@inject IIdentityService IdentityService +@using MudBlazor + +Privacy +@if (IsLoading) +{ + +} +else +{ + + + + + + + + Save privacy +} + +@code { + [Parameter] + public bool IsLoading { get; set; } + + [Parameter] + public StudentWithGalleryImagesDto StudentWithGalleryImagesDto { get; set; } + + [Parameter] + public EventCallback SaveChangesAsync { get; set; } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ProfileComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ProfileComponent.razor new file mode 100644 index 000000000..f4f7091c5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ProfileComponent.razor @@ -0,0 +1,446 @@ +@page "/profile" +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject IMediaFilesService MediaFilesService +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.Utilities +@using System.IO +@using MudBlazor +@using System + +Profile +@if (IsLoading) +{ + +} +else +{ + + + +
+
+
+ + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + Education + @foreach (var education in StudentWithGalleryImagesDto.Student.Education) + { + + + + + + + + + + + + + + + + + + + + + + + + + Remove + + + + + } + + Add Education + + + + + + Work Experience + @foreach (var work in StudentWithGalleryImagesDto.Student.Work) + { + + + + + + + + + + + + + + + + + + + + + + + + + Remove + + + + + } + + Add Work Experience + + + +
+ + + Save profile + + + @if (IsUploading) + { + + } +} + + + + + + + +@code { + [Parameter] public bool IsLoading { get; set; } + [Parameter] public IBrowserFile File { get; set; } + [Parameter] public StudentWithGalleryImagesDto StudentWithGalleryImagesDto { get; set; } + [Parameter] public EventCallback SaveChangesAsync { get; set; } + [Parameter] public EventCallback SaveImageAsync { get; set; } + [Parameter] public bool IsUploading { get; set; } + private string CroppedImageBase64 { get; set; } + + private IBrowserFile croppedImageFile; + private string currentImageType = string.Empty; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", DotNetObjectReference.Create(this)); + } + } + + private string GetImage() + { + return !string.IsNullOrEmpty(StudentWithGalleryImagesDto.Student.ProfileImageUrl) + ? StudentWithGalleryImagesDto.Student.ProfileImageUrl + : "images/default_profile_image.webp"; + } + + private string GetBanner() + { + return !string.IsNullOrEmpty(StudentWithGalleryImagesDto.Student.BannerUrl) + ? StudentWithGalleryImagesDto.Student.BannerUrl + : "images/default_banner_image.png"; + } + + private async Task OpenCropper(InputFileChangeEventArgs e, string imageType) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageType = imageType; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, imageType); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); + + try + { + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + string imageType = currentImageType == "profile" ? "StudentProfileImage" : "StudentBannerImage"; + var response = await MediaFilesService.UploadMediaFileAsync( + StudentWithGalleryImagesDto.Student.Id, + imageType, + IdentityService.GetCurrentUserId(), + $"{StudentWithGalleryImagesDto.Student.FirstName}_{StudentWithGalleryImagesDto.Student.LastName}_{currentImageType}.png", + croppedImageFile.ContentType, + fileData); + + StateHasChanged(); + await SaveImageAsync.InvokeAsync(null); + + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + CloseCropper(); + } + } + + private async Task RemoveImage(string imageType) + { + IsUploading = true; + StateHasChanged(); + + try + { + string imageUrl = imageType == "profile" ? StudentWithGalleryImagesDto.Student.ProfileImageUrl : StudentWithGalleryImagesDto.Student.BannerUrl; + await MediaFilesService.DeleteMediaFileAsync(imageUrl); + if (imageType == "profile") + { + StudentWithGalleryImagesDto.Student.ProfileImageUrl = null; + } + else + { + StudentWithGalleryImagesDto.Student.BannerUrl = null; + } + await SaveImageAsync.InvokeAsync(null); + + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred while removing the image: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + } + + private void AddEducation() + { + var educationList = StudentWithGalleryImagesDto.Student.Education.ToList(); + educationList.Add(new EducationDto + { + InstitutionName = string.Empty, + Degree = string.Empty, + StartDate = null, + EndDate = null, + Description = string.Empty + }); + StudentWithGalleryImagesDto.Student.Education = educationList; + StateHasChanged(); + } + + private void RemoveEducation(EducationDto education) + { + var educationList = StudentWithGalleryImagesDto.Student.Education.ToList(); + educationList.Remove(education); + StudentWithGalleryImagesDto.Student.Education = educationList; + StateHasChanged(); + } + + + private void AddWorkExperience() + { + var workList = StudentWithGalleryImagesDto.Student.Work.ToList(); + workList.Add(new WorkDto + { + Company = string.Empty, + Position = string.Empty, + StartDate = null, // Initialize as null + EndDate = null, // Initialize as null + Description = string.Empty + }); + + StudentWithGalleryImagesDto.Student.Work = workList; + StateHasChanged(); + } + + + private void RemoveWorkExperience(WorkDto work) + { + var workList = StudentWithGalleryImagesDto.Student.Work.ToList(); + workList.Remove(work); + StudentWithGalleryImagesDto.Student.Work = workList; + StateHasChanged(); + } + + private async Task SaveProfile() + { + // Ensure dates are correct before saving + foreach (var education in StudentWithGalleryImagesDto.Student.Education) + { + // Log the values to verify correctness + Console.WriteLine($"Before Save - Education StartDate: {education.StartDate}, EndDate: {education.EndDate}"); + } + + foreach (var work in StudentWithGalleryImagesDto.Student.Work) + { + // Log the values to verify correctness + Console.WriteLine($"Before Save - Work StartDate: {work.StartDate}, EndDate: {work.EndDate}"); + } + + + // Invoke the parent method to save changes + await SaveChangesAsync.InvokeAsync(null); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ResetPassword.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ResetPassword.razor new file mode 100644 index 000000000..8107106e9 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ResetPassword.razor @@ -0,0 +1,181 @@ +@page "/reset-password/" +@page "/reset-password/{Token}/page" +@using Astravent.Web.Wasm.Models.Identity +@using Astravent.Web.Wasm.Areas.Identity +@using Astravent.Web.Wasm.Areas.Http +@using Astravent.Web.Wasm.DTO +@using MudBlazor +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime + + + +
+
+ +
+
+
+
+

Reset Your Password

+

Please enter your new password below.

+
+ + + + + + + + + + + + + Reset Password + + + + @if (showError) + { + + @errorMessage + + } +
+ Back to Sign In +
+
+
+
+ +@code { + [Parameter] + public string Token { get; set; } = ""; + + private ResetPasswordModel resetPasswordModel = new ResetPasswordModel(); + private bool showError = false; + private string errorMessage = string.Empty; + private InputType passwordInputType = InputType.Password; + private string passwordInputIcon = Icons.Material.Filled.VisibilityOff; + + protected override void OnInitialized() + { + Console.WriteLine($"Current URL: {NavigationManager.Uri}"); + Console.WriteLine($"Token received: {Token}"); + + if (string.IsNullOrEmpty(Token)) + { + showError = true; + errorMessage = "Invalid token."; + } + else + { + resetPasswordModel.Token = Token; + } + } + + private void OnAlertClose() + { + showError = false; + errorMessage = string.Empty; + } + + private void TogglePasswordVisibility() + { + if (passwordInputType == InputType.Password) + { + passwordInputType = InputType.Text; + passwordInputIcon = Icons.Material.Filled.Visibility; + } + else + { + passwordInputType = InputType.Password; + passwordInputIcon = Icons.Material.Filled.VisibilityOff; + } + } + + private async Task HandleResetPassword() + { + Console.WriteLine($"NewPassword: {resetPasswordModel.NewPassword}, ConfirmPassword: {resetPasswordModel.ConfirmPassword}"); + try + { + var response = await IdentityService.ResetPasswordAsync(resetPasswordModel.Token, resetPasswordModel.Email, resetPasswordModel.NewPassword); + if (response != null) + { + NavigationManager.NavigateTo("/signin", true); + } + else + { + showError = true; + errorMessage = "Failed to reset password. Please try again."; + StateHasChanged(); + } + } + catch (Exception ex) + { + showError = true; + errorMessage = $"Error during password reset: {ex.Message}"; + StateHasChanged(); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ResetPasswordConfirm.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ResetPasswordConfirm.razor new file mode 100644 index 000000000..45c0d5b80 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ResetPasswordConfirm.razor @@ -0,0 +1,81 @@ +@page "/reset-password-confirm" +@using Astravent.Web.Wasm.Models.Identity +@using Astravent.Web.Wasm.Areas.Identity +@using Astravent.Web.Wasm.Areas.Http +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime + + + +
+
+ +
+
+
+ + + +
+
+
diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SecurityComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SecurityComponent.razor new file mode 100644 index 000000000..ba0455b68 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SecurityComponent.razor @@ -0,0 +1,92 @@ +@page "/security" +@inject IIdentityService IdentityService +@using MudBlazor + +Security +@if (IsLoading) +{ + +} +else +{ + + + + + + To enable Two-Factor Authentication (2FA), switch on the button below. Once activated, a button to generate a 2FA secret code will appear. + Click this button to generate your security code. After the code is generated, click the "Save 2FA Settings" button to enable 2FA on your account. + When logging in with 2FA enabled, you will be required to enter a code that you will receive via email after attempting to log in. + Input the code on the screen you will be redirected to after logging in. + + + + + + @if (IsTwoFactorEnabled) + { + @if (string.IsNullOrEmpty(TwoFactorSecret)) + { +
+ + Generate Secret Token + +
+ + } + else + { + + This secret will be used to set up your two-factor authentication. + } + } +
+ + Save 2FA Settings +} + +@code { + [Parameter] public bool IsLoading { get; set; } + [Parameter] public bool IsTwoFactorEnabled { get; set; } + [Parameter] public string TwoFactorSecret { get; set; } + [Parameter] public EventCallback ToggleTwoFactor { get; set; } + + private async Task GenerateTwoFactorSecret() + { + try + { + var userId = IdentityService.GetCurrentUserId(); + TwoFactorSecret = await IdentityService.GenerateTwoFactorSecretAsync(userId); + StateHasChanged(); + } + catch (Exception ex) + { + Console.WriteLine($"Error generating two-factor secret: {ex.Message}"); + } + } + + private async Task SaveTwoFactorSettingsAsync() + { + try + { + if (IsTwoFactorEnabled) + { + if (string.IsNullOrEmpty(TwoFactorSecret)) + { + throw new InvalidOperationException("Secret token must be generated before enabling 2FA."); + } + await IdentityService.EnableTwoFactorAsync(IdentityService.GetCurrentUserId(), TwoFactorSecret); + } + else + { + await IdentityService.DisableTwoFactorAsync(IdentityService.GetCurrentUserId()); + TwoFactorSecret = null; // Clear the secret if 2FA is disabled + } + StateHasChanged(); // Update the UI after saving the settings + } + catch (Exception ex) + { + Console.WriteLine($"Error saving 2FA settings: {ex.Message}"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ShowAccount.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ShowAccount.razor new file mode 100644 index 000000000..14f5a454d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ShowAccount.razor @@ -0,0 +1,449 @@ +@page "/account" +@using System.Globalization +@using Astravent.Web.Wasm.Shared +@using Astravent.Web.Wasm.Areas.Students +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.MediaFiles +@using Astravent.Web.Wasm.DTO.Types +@using MudBlazor +@using System.IO +@using System.Text.Json +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject IMediaFilesService MediaFilesService +@inject NavigationManager NavigationManager + + + + + + + +@code { + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Account settings", href: "/events/follow", disabled: true, icon: @Icons.Material.Filled.ManageAccounts), + }; + + private int activeTabIndex = 0; + private bool isTwoFactorEnabled; + private string twoFactorSecret; + private bool isLoading = true; + private bool isUploading = false; + private IBrowserFile file; + private long maxFileSize = 10 * 1024 * 1024; + private StudentWithGalleryImagesDto studentWithGalleryImagesDto = new(); + private NotificationPreferencesDto notificationPreferencesDto = new NotificationPreferencesDto(); + private AvailableSettingsDto availableSettingsDto = new AvailableSettingsDto(); + + private HashSet selectedLanguages = new HashSet(); + private HashSet selectedInterests = new HashSet(); + + protected override async Task OnInitializedAsync() + { + isLoading = true; + StateHasChanged(); + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + var studentId = IdentityService.GetCurrentUserId(); + studentWithGalleryImagesDto = await StudentsService.GetStudentWithGalleryImagesAsync(studentId); + var studentDto = studentWithGalleryImagesDto.Student; + + var studentDtoJson = JsonSerializer.Serialize(studentDto, new JsonSerializerOptions { WriteIndented = true }); + Console.WriteLine($"StudentDto object: {studentDtoJson}"); + + if (studentDto.EmailNotifications) + { + notificationPreferencesDto = await StudentsService.GetUserNotificationPreferencesAsync(studentId); + Console.WriteLine(JsonSerializer.Serialize(notificationPreferencesDto)); + } + isTwoFactorEnabled = studentDto.IsTwoFactorEnabled; + twoFactorSecret = studentDto.IsTwoFactorEnabled ? studentDto.TwoFactorSecret : null; + + availableSettingsDto = await StudentsService.GetUserSettingsAsync(studentId); + + selectedLanguages = new HashSet(studentDto.Languages); + selectedInterests = new HashSet(studentDto.Interests); + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + isLoading = false; + StateHasChanged(); + } + } + + private void OnSelectedLanguagesChanged(IEnumerable languages) + { + selectedLanguages = new HashSet(languages); + } + + private void OnSelectedInterestsChanged(IEnumerable interests) + { + selectedInterests = new HashSet(interests); + } + + private async Task SaveChanges() + { + var studentDto = studentWithGalleryImagesDto.Student; + studentDto.Languages = selectedLanguages.ToList(); + studentDto.Interests = selectedInterests.ToList(); + + await StudentsService.UpdateStudentLanguagesAndInterestsAsync( + studentDto.Id, + studentDto.Languages, + studentDto.Interests + ); + + await StudentsService.UpdateStudentDto(studentDto.Id); + StateHasChanged(); + } + + private async Task SaveChangesAsync() + { + try + { + var studentDto = studentWithGalleryImagesDto.Student; + + foreach (var education in studentDto.Education) + { + Console.WriteLine($"Education StartDate: {education.StartDate}, EndDate: {education.EndDate}"); + } + + foreach (var work in studentDto.Work) + { + Console.WriteLine($"Work StartDate: {work.StartDate}, EndDate: {work.EndDate}"); + } + + + // Ensure that StartDate and EndDate are handled correctly + foreach (var education in studentDto.Education) + { + education.StartDate = education.StartDate != DateTime.MinValue ? education.StartDate : null; + education.EndDate = education.EndDate != DateTime.MinValue ? education.EndDate : null; + } + + foreach (var work in studentDto.Work) + { + work.StartDate = work.StartDate != DateTime.MinValue ? work.StartDate : null; + work.EndDate = work.EndDate != DateTime.MinValue ? work.EndDate : null; + } + + var updateStudentData = new + { + studentDto.Id, + studentDto.FirstName, + studentDto.LastName, + studentDto.ProfileImageUrl, + studentDto.Description, + studentDto.EmailNotifications, + studentDto.ContactEmail, + studentDto.Languages, + studentDto.Interests, + studentDto.Education, + studentDto.Work, + studentDto.PhoneNumber, + studentDto.Country, + studentDto.City, + studentDto.DateOfBirth, + IsTwoFactorEnabled = isTwoFactorEnabled, + TwoFactorSecret = isTwoFactorEnabled ? twoFactorSecret : null + }; + + var jsonData = JsonSerializer.Serialize(updateStudentData); + Console.WriteLine($"Sending UpdateStudent request: {jsonData}"); + + await StudentsService.UpdateStudentAsync( + studentDto.Id, + studentDto.FirstName, + studentDto.LastName, + studentDto.ProfileImageUrl, + studentDto.Description, + studentDto.EmailNotifications, + studentDto.ContactEmail, + studentDto.Languages, + studentDto.Interests, + isTwoFactorEnabled, + !isTwoFactorEnabled, + isTwoFactorEnabled ? twoFactorSecret : null, + studentDto.Education, + studentDto.Work, + studentDto.PhoneNumber, + studentDto.Country, + studentDto.City, + studentDto.DateOfBirth + ); + + if (studentDto.EmailNotifications) + { + await StudentsService.UpdateUserNotificationPreferencesAsync(studentDto.Id, notificationPreferencesDto, studentDto.EmailNotifications); + } + + await StudentsService.UpdateUserSettingsAsync(studentDto.Id, availableSettingsDto); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + + + + + + + private async Task SaveTwoFactorSettingsAsync() + { + try + { + if (isTwoFactorEnabled) + { + if (string.IsNullOrEmpty(twoFactorSecret)) + { + throw new InvalidOperationException("Secret token must be generated before enabling 2FA."); + } + + await IdentityService.EnableTwoFactorAsync(IdentityService.GetCurrentUserId(), twoFactorSecret); + } + else + { + await IdentityService.DisableTwoFactorAsync(IdentityService.GetCurrentUserId()); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error saving 2FA settings: {ex.Message}"); + } + } + + private async Task ToggleTwoFactor(bool enabled) + { + isTwoFactorEnabled = enabled; + if (!enabled) + { + await IdentityService.DisableTwoFactorAsync(IdentityService.GetCurrentUserId()); + twoFactorSecret = null; + } + else + { + twoFactorSecret = await IdentityService.GenerateTwoFactorSecretAsync(IdentityService.GetCurrentUserId()); + } + StateHasChanged(); + } + + private async Task GenerateTwoFactorSecret() + { + var userId = IdentityService.GetCurrentUserId(); + twoFactorSecret = await IdentityService.GenerateTwoFactorSecretAsync(userId); + StateHasChanged(); + } + + private async Task SaveNotificationPreferencesAsync() + { + try + { + var studentId = IdentityService.GetCurrentUserId(); + await StudentsService.UpdateUserNotificationPreferencesAsync(studentId, notificationPreferencesDto, studentWithGalleryImagesDto.Student.EmailNotifications); + Console.WriteLine("Notification preferences updated successfully."); + } + catch (Exception ex) + { + Console.WriteLine($"Error saving notification preferences: {ex.Message}"); + } + } + + private async Task SaveUserSettingsAsync() +{ + try + { + var studentId = IdentityService.GetCurrentUserId(); + + @* var jsonSettings = JsonSerializer.Serialize(availableSettingsDto, new JsonSerializerOptions { WriteIndented = true }); + + Console.WriteLine("Sending the following settings to the student service:"); + Console.WriteLine(jsonSettings); *@ + + await StudentsService.UpdateUserSettingsAsync(studentId, availableSettingsDto); + + Console.WriteLine("Student settings updated successfully."); + } + catch (Exception ex) + { + Console.WriteLine($"Error saving user settings: {ex.Message}"); + } +} + + + private void SetActiveTabIndex(int index) + { + activeTabIndex = index; + } + + private static async Task ReadFully(Stream input) + { + byte[] buffer = new byte[16 * 1024]; + using (MemoryStream ms = new MemoryStream()) + { + int read; + while ((read = await input.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + ms.Write(buffer, 0, read); + } + return ms.ToArray(); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ShowAccountOLD.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ShowAccountOLD.razor new file mode 100644 index 000000000..42036db0b --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/ShowAccountOLD.razor @@ -0,0 +1,293 @@ +@* @page "/account" +@using System.Globalization +@using Astravent.Web.Wasm.Areas.Students +@using Astravent.Web.Wasm.Components +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.MediaFiles +@using Astravent.Web.Wasm.DTO.Types +@using Astravent.Web.Wasm.Shared +@using Radzen +@using System.IO +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject IMediaFilesService MediaFilesService +@inject NavigationManager NavigationManager +@using MudBlazor + + + + +@code { + private List _items = new List + { + new BreadcrumbItem("Home", href: "/", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Account settings", href: "/events/follow", disabled: true, icon: @Icons.Material.Filled.ManageAccounts), + }; + private StudentDto studentDto = new(); + private bool editionDisabled = true; + private string profileImage = string.Empty; + private TaskCompletionSource clientChangeCompletionSource; + private bool isUploading = false; + private bool isLoading = true; + + private const string dateFormat = "dd/MM/yyyy HH:mm"; + private const string shortDateFormat = "dd/MM/yyyy"; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + StateHasChanged(); + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + var studentId = IdentityService.GetCurrentUserId(); + studentDto = await StudentsService.GetStudentAsync(studentId); + profileImage = studentDto.ProfileImageUrl; // Directly using the URL from the DTO + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + finally + { + isLoading = false; + StateHasChanged(); + } + } + + void EnableEdition() + { + editionDisabled = false; + StateHasChanged(); + } + + private async Task HandleUpdateStudent() + { + if (clientChangeCompletionSource != null) + { + await clientChangeCompletionSource.Task; + } + editionDisabled = true; + await StudentsService.UpdateStudentAsync(studentDto.Id, studentDto.ProfileImageUrl, + studentDto.Description, studentDto.EmailNotifications, studentDto.ContactEmail); + await OnInitializedAsync(); + } + + private string GetImage() + { + if (!string.IsNullOrEmpty(profileImage)) + { + return profileImage; + } + + return "images/default_profile_image.webp"; + } + + async void OnClientChange(UploadChangeEventArgs args) + { + clientChangeCompletionSource = new TaskCompletionSource(); + + foreach (var file in args.Files) + { + isUploading = true; + StateHasChanged(); + + try + { + long maxFileSize = 10 * 1024 * 1024; + var stream = file.OpenReadStream(maxFileSize); + byte[] bytes = await ReadFully(stream); + var base64Content = Convert.ToBase64String(bytes); + var response = await MediaFilesService.UploadMediaFileAsync(IdentityService.UserDto.Id, + MediaFileContextType.StudentProfileImage.ToString(), IdentityService.UserDto.Id, + file.Name, file.ContentType, base64Content); + if (response.Content != null && !string.IsNullOrEmpty(response.Content.FileUrl)) + { + studentDto.ProfileImageUrl = response.Content.FileUrl; + profileImage = response.Content.FileUrl; // Update the local profile image URL + } + + stream.Close(); + clientChangeCompletionSource.SetResult(true); + } + catch (Exception ex) + { + clientChangeCompletionSource.SetResult(false); + } + finally + { + isUploading = false; + StateHasChanged(); + } + } + } + + private static async Task ReadFully(Stream input) + { + byte[] buffer = new byte[16 * 1024]; + using (MemoryStream ms = new MemoryStream()) + { + int read; + while ((read = await input.ReadAsync(buffer, 0, buffer.Length)) > 0) + { + ms.Write(buffer, 0, read); + } + return ms.ToArray(); + } + } +} + + + +@if (isLoading) +{ +
+ + + +
+} +else if (studentDto.Id != Guid.Empty) +{ + + @if (studentDto.State == "valid") + { + + + + } + + + + + + + + + + + + + +} +else +{ +
+ + + +
+} +
+ + *@ diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignIn.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignIn.razor new file mode 100644 index 000000000..afbed32bf --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignIn.razor @@ -0,0 +1,251 @@ +@page "/signin" +@using Astravent.Web.Wasm.Models.Identity +@using Astravent.Web.Wasm.Areas.Identity +@using Astravent.Web.Wasm.Areas.Students +@using Astravent.Web.Wasm.Areas.Http +@using MudBlazor +@using MudBlazor.Extensions +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject IErrorMapperService ErrorMapperService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime + + + +
+
+ +
+
+
+ + @if (showError) + { + + @errorMessage + + } + @if (isTwoFactorRequired) + { + + + + + + + Submit + + + + } + else + { + + + + + + + + + + + Forgot Password? + + + Sign In + + + +
+ Create Account +
+ } +
+
+
+ +@code { + private SignInModel signInModel = new SignInModel(); + private TwoFactorModel twoFactorModel = new TwoFactorModel(); + private bool showError = false; + private string errorMessage = string.Empty; + private bool rememberMe = false; + private MudBlazor.InputType passwordInputType = MudBlazor.InputType.Password; + private string passwordInputIcon = MudBlazor.Icons.Material.Filled.VisibilityOff; + private bool isTwoFactorRequired = false; + private Guid userId; + private string deviceType; + + protected override async Task OnInitializedAsync() + { + deviceType = await JSRuntime.InvokeAsync("getDeviceType"); + } + + private void OnAlertClose() + { + showError = false; + errorMessage = string.Empty; + } + + private void TogglePasswordVisibility() + { + if (passwordInputType == MudBlazor.InputType.Password) + { + passwordInputType = MudBlazor.InputType.Text; + passwordInputIcon = MudBlazor.Icons.Material.Filled.Visibility; + } + else + { + passwordInputType = MudBlazor.InputType.Password; + passwordInputIcon = MudBlazor.Icons.Material.Filled.VisibilityOff; + } + } + + private async Task HandleSignIn() + { + try + { + var deviceType = await JSRuntime.InvokeAsync("getDeviceType"); + + var response = await IdentityService.SignInAsync(signInModel.Email, signInModel.Password, deviceType); + + + if (response != null && response.Content != null) + { + if (response.Content.IsTwoFactorRequired) + { + isTwoFactorRequired = true; + userId = response.Content.UserId; + twoFactorModel.Code = string.Empty; + StateHasChanged(); + } + else if (!string.IsNullOrEmpty(response.Content.AccessToken)) + { + await StudentsService.UpdateStudentDto(IdentityService.UserDto.Id); + var nextPage = StudentsService.StudentDto.State == "incomplete" ? "/signin/first" : "/home"; + NavigationManager.NavigateTo(nextPage, forceLoad: true); + } + else + { + showError = true; + errorMessage = $"Error during sign in: {response?.ErrorMessage?.Reason ?? "Unknown error"}"; + StateHasChanged(); + } + } + else + { + showError = true; + errorMessage = $"Error during sign in: {response?.ErrorMessage?.Reason ?? "Unknown error"}"; + StateHasChanged(); + } + } + catch (Exception ex) + { + showError = true; + errorMessage = $"An unexpected error occurred: {ex.Message} or User not found in our database."; + StateHasChanged(); + } + } + + private async Task HandleTwoFactor() + { + try + { + var deviceType = await JSRuntime.InvokeAsync("getDeviceType"); + + var response = await IdentityService.VerifyTwoFactorCodeAsync(userId, twoFactorModel.Code, deviceType); + + if (response != null && response.Content != null && !string.IsNullOrEmpty(response.Content.AccessToken)) + { + await StudentsService.UpdateStudentDto(IdentityService.UserDto.Id); + var nextPage = StudentsService.StudentDto.State == "incomplete" ? "/signin/first" : "/home"; + NavigationManager.NavigateTo(nextPage, true); + } + else + { + showError = true; + errorMessage = $"Error during 2FA verification: {response?.ErrorMessage?.Reason ?? "Unknown error"}"; + StateHasChanged(); + } + } + catch (Exception ex) + { + showError = true; + errorMessage = $"Error during 2FA verification: {ex.Message}"; + StateHasChanged(); + } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignInComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignInComponent.razor new file mode 100644 index 000000000..3c740fbc8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignInComponent.razor @@ -0,0 +1,103 @@ +@page "/signin-verify" +@using Astravent.Web.Wasm.Models.Identity +@using Astravent.Web.Wasm.Areas.Identity +@using MudBlazor +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime + +
+ + @if (showError) + { + + @errorMessage + + } + + + + + + + + + + + Forgot Password? + + + Sign In + + + +
+ Create Account +
+
+ +@code { + private SignInModel signInModel = new SignInModel(); + private bool showError = false; + private string errorMessage = string.Empty; + private bool rememberMe = false; + private InputType passwordInputType = InputType.Password; + private string passwordInputIcon = Icons.Material.Filled.VisibilityOff; + private string deviceType; + + protected override async Task OnInitializedAsync() + { + deviceType = await JSRuntime.InvokeAsync("getDeviceType"); + } + + private void OnAlertClose() + { + showError = false; + errorMessage = string.Empty; + } + + private void TogglePasswordVisibility() + { + if (passwordInputType == InputType.Password) + { + passwordInputType = InputType.Text; + passwordInputIcon = Icons.Material.Filled.Visibility; + } + else + { + passwordInputType = InputType.Password; + passwordInputIcon = Icons.Material.Filled.VisibilityOff; + } + } + + private async Task HandleSignIn() + { + try + { + var response = await IdentityService.SignInAsync(signInModel.Email, signInModel.Password, deviceType); + + if (response != null && response.Content != null && !string.IsNullOrEmpty(response.Content.AccessToken)) + { + NavigationManager.NavigateTo("/home", true); + } + else + { + showError = true; + errorMessage = $"Error during sign in: {response?.ErrorMessage?.Reason}"; + StateHasChanged(); + } + } + catch (Exception ex) + { + showError = true; + errorMessage = $"Error during sign in: {ex.Message}"; + StateHasChanged(); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignUp.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignUp.razor new file mode 100644 index 000000000..d97e68e5c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/SignUp.razor @@ -0,0 +1,176 @@ +@page "/signup" +@using Astravent.Web.Wasm.Models.Identity +@using Astravent.Web.Wasm.Areas.Identity +@using Astravent.Web.Wasm.Areas.Students +@using Astravent.Web.Wasm.Areas.Http +@using MudBlazor +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@inject IErrorMapperService ErrorMapperService + + + + +
+
+ +
+
+
+ + @if (showError) + { + + @errorMessage + + } + + + + + + + + + + + + + + + + + + + Sign Up + + + +
+ Already have an account? Sign In +
+
+
+
+ +@code { + private SignUpModel signUpModel = new SignUpModel(); + private bool showError = false; + private string errorMessage = string.Empty; + private InputType passwordInputType = InputType.Password; + private string passwordInputIcon = Icons.Material.Filled.VisibilityOff; + + private void OnAlertClose() + { + showError = false; + errorMessage = string.Empty; + } + + private void TogglePasswordVisibility() + { + if (passwordInputType == InputType.Password) + { + passwordInputType = InputType.Text; + passwordInputIcon = Icons.Material.Filled.Visibility; + } + else + { + passwordInputType = InputType.Password; + passwordInputIcon = Icons.Material.Filled.VisibilityOff; + } + } + + private async Task HandleSignUp() + { + try + { + if (signUpModel.Password != signUpModel.ConfirmPassword) + { + showError = true; + errorMessage = "Passwords do not match."; + return; + } + + var response = await IdentityService.SignUpAsync(signUpModel.FirstName, signUpModel.LastName, signUpModel.Email, signUpModel.Password, "user"); + if (response.ErrorMessage == null) + { + NavigationManager.NavigateTo("/email-verification-info"); + } + else + { + showError = true; + errorMessage = ErrorMapperService.MapError(response.ErrorMessage); + } + } + catch (Exception ex) + { + showError = true; + errorMessage = $"Error during sign up: {ex.Message}"; + StateHasChanged(); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/UserSettingsComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/UserSettingsComponent.razor new file mode 100644 index 000000000..250f601ba --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Account/UserSettingsComponent.razor @@ -0,0 +1,143 @@ +@page "/user-settings" +@inject IIdentityService IdentityService +@using MudBlazor + +User Settings +@if (IsLoading) +{ + +} +else +{ + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + @foreach (Visibility visibility in Enum.GetValues(typeof(Visibility))) + { + @visibility + } + + + + + + @foreach (PreferredLanguage language in Enum.GetValues(typeof(PreferredLanguage))) + { + @language + } + + + + + @foreach (FrontendVersion version in Enum.GetValues(typeof(FrontendVersion))) + { + @version + } + + + + Save Settings +} + +@code { + [Parameter] public bool IsLoading { get; set; } + [Parameter] public AvailableSettingsDto AvailableSettingsDto { get; set; } + [Parameter] public EventCallback SaveUserSettingsAsync { get; set; } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/ChatPage.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/ChatPage.razor new file mode 100644 index 000000000..c4cfeb602 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/ChatPage.razor @@ -0,0 +1,759 @@ +@page "/chats/{ChatId:guid}" +@using Astravent.Web.Wasm.HttpClients +@using Astravent.Web.Wasm.Areas.Communication +@using Astravent.Web.Wasm.DTO.Communication +@inject IIdentityService IdentityService +@inject ICommunicationService CommunicationService +@inject IStudentsService StudentsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using MudBlazor +@using System.Text.Json +@inject ChatSignalRService ChatSignalRService +@inject IJSRuntime JSRuntime +@implements IAsyncDisposable +@using System.Threading; + + + + + + + + + @foreach (var chat in userChats) + { + +
+ + + @if (GetUnreadMessageCount(chat.Id) > 0) + { + + } + +
+ @GetChatName(chat.Id) + @if (IsUserTypingInChat(chat.Id)) + { + @typingUserName is typing... + } + else + { + @GetLastMessagePreview(chat.Id) + @GetLastMessageTime(chat.Id) + } +
+
+
+ } +
+
+
+ + + + +
+ @if (messages != null) + { + @foreach (var message in messages) + { +
+
+ + + +
+ +
+
+ @GetSenderName(message.SenderId) + @message.Timestamp.ToString("HH:mm") +
+
@message.Content
+
+ +
+
+
+ } + } +
+ + @if (isUserTyping && typingUserName != null) + { +
+ @typingUserName is typing... +
+ } + +
+ + +
+ + @if (!isConnected) + { + + You are currently disconnected. Some features may be unavailable. + + } +
+
+
+ + @if (!isConnected) + { +
+ + Connecting to chat... +
+ } + +
+ + +
+ + + + +@code { + [Parameter] public Guid ChatId { get; set; } + + private List userChats = new(); + private List messages = new(); + private string newMessageContent = string.Empty; + private Dictionary userNames = new(); + private Dictionary userImages = new(); + private Dictionary lastMessages = new(); + private Dictionary typingStatus = new(); + private bool isSending = false; + private bool hasUpdatedStatus = false; + private bool isUserTyping = false; + private string typingUserName = string.Empty; + private Timer typingTimer; + private bool isConnected = false; // Start as not connected + private bool IsSendButtonDisabledCombined => IsSendButtonDisabled || !isConnected; + + private bool IsSendButtonDisabled => isSending || string.IsNullOrWhiteSpace(newMessageContent); + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + var userId = IdentityService.GetCurrentUserId(); + ChatSignalRService.ConnectionChanged += OnConnectionChanged; + ChatSignalRService.MessageReceived += OnMessageReceived; + ChatSignalRService.MessageStatusUpdated += OnMessageStatusUpdated; + ChatSignalRService.TypingNotificationReceived += OnTypingNotificationReceived; + + await InitializeSignalRConnection(userId); + + if (isConnected) + { + await LoadUserChats(); + if (ChatId != Guid.Empty) + { + await LoadMessages(ChatId); + } + } + else + { + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + protected override async Task OnParametersSetAsync() + { + if (isConnected) + { + await LoadMessages(ChatId); + } + } + + private async Task InitializeSignalRConnection(Guid userId) + { + if (!isConnected) + { + await ChatSignalRService.StartAsync(userId, ChatId); + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await ScrollToBottomAsync(); + } + else + { + await JSRuntime.InvokeVoidAsync("scrollToBottom", "chatMessagesContainer"); + } + + if (!hasUpdatedStatus) + { + hasUpdatedStatus = true; + await Task.Delay(1000); + await UpdateUnreadMessagesStatusAsync(); + } + } + + private async void OnTypingTimeout(object state) + { + await InvokeAsync(() => + { + if (state is Guid chatId) + { + typingStatus[chatId] = false; + isUserTyping = false; + typingUserName = string.Empty; + StateHasChanged(); + } + }); + + typingTimer?.Dispose(); + } + + private async Task HandleInputChange(ChangeEventArgs e) + { + if (e.Value is string inputValue) + { + await ChatSignalRService.SendTypingNotificationAsync(!string.IsNullOrEmpty(inputValue)); + + typingTimer?.Dispose(); + typingTimer = new Timer(OnTypingTimeout, ChatId, 1000, Timeout.Infinite); + } + } + + private async void OnTypingNotificationReceived(string userId, bool isTyping) + { + await InvokeAsync(() => + { + var chatId = ChatId; + + if (userNames.TryGetValue(Guid.Parse(userId), out var userName)) + { + typingUserName = userName; + } + else + { + typingUserName = "Unknown User"; + } + + isUserTyping = isTyping; + typingStatus[chatId] = isTyping; + + if (isTyping) + { + typingTimer?.Dispose(); + typingTimer = new Timer(OnTypingTimeout, chatId, 1000, Timeout.Infinite); + } + + StateHasChanged(); + }); + } + + private async void OnConnectionChanged(bool connected) + { + await InvokeAsync(async () => + { + isConnected = connected; + if (isConnected) + { + await LoadUserChats(); + if (ChatId != Guid.Empty) + { + await LoadMessages(ChatId); + } + StateHasChanged(); + } + }); + } + + private async Task LoadUserChats() + { + try + { + var userId = IdentityService.GetCurrentUserId(); + var result = await CommunicationService.GetUserChatsAsync(userId, 1, 20); + + if (result != null) + { + userChats = result.Items.SelectMany(u => u.Chats).ToList(); + await LoadUserDetails(); + await LoadLastMessages(); + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load chats: {ex.Message}", Severity.Error); + } + } + + private async Task LoadMessages(Guid chatId) + { + try + { + messages = (await CommunicationService.GetMessagesForChatAsync(chatId)).ToList(); + await LoadUserDetails(); + await ScrollToBottomAsync(); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load messages: {ex.Message}", Severity.Error); + } + } + + private async Task LoadUserDetails() + { + var senderIds = messages.Select(m => m.SenderId).Distinct().ToList(); + var chatUserIds = userChats.SelectMany(c => c.ParticipantIds).Distinct().ToList(); + var allUserIds = senderIds.Union(chatUserIds).Distinct().ToList(); + + foreach (var userId in allUserIds) + { + if (!userNames.ContainsKey(userId)) + { + var user = await StudentsService.GetStudentAsync(userId); + if (user != null) + { + userNames[userId] = $"{user.FirstName} {user.LastName}"; + userImages[userId] = string.IsNullOrWhiteSpace(user.ProfileImageUrl) ? "images/default_profile_image.webp" : user.ProfileImageUrl; + } + } + } + } + + private async Task LoadLastMessages() + { + foreach (var chat in userChats) + { + var messages = await CommunicationService.GetMessagesForChatAsync(chat.Id); + lastMessages[chat.Id] = messages.OrderByDescending(m => m.Timestamp).FirstOrDefault(); + } + StateHasChanged(); + } + + private async Task SendMessage() + { + if (!string.IsNullOrWhiteSpace(newMessageContent) && !isSending) + { + isSending = true; + + try + { + var userId = IdentityService.GetCurrentUserId(); + var command = new SendMessageCommand(ChatId, userId, newMessageContent); + + var response = await CommunicationService.SendMessageAsync(command); + if (response.IsSuccessStatusCode) + { + newMessageContent = string.Empty; + Snackbar.Add("Message sent!", Severity.Success); + await ScrollToBottomAsync(); + } + else + { + Snackbar.Add("Failed to send message.", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Error: {ex.Message}", Severity.Error); + } + finally + { + isSending = false; + } + } + } + + private async Task ScrollToBottomAsync() + { + await JSRuntime.InvokeVoidAsync("scrollToBottom", "chatMessagesContainer"); + } + + private async Task UpdateUnreadMessagesStatusAsync() + { + var unreadMessages = messages + .Where(m => m.SenderId != IdentityService.GetCurrentUserId() && m.Status != "Read") + .ToList(); + + if (unreadMessages.Any()) + { + foreach (var message in unreadMessages) + { + await UpdateMessageStatus(message, "Read"); + } + } + } + + private async Task UpdateMessageStatus(MessageDto message, string status) + { + if (message.ChatId == Guid.Empty || message.Id == Guid.Empty) + { + Snackbar.Add("Invalid message ID or chat ID.", Severity.Error); + return; + } + + var command = new UpdateMessageStatusCommand(message.ChatId, message.Id, status); + var response = await CommunicationService.UpdateMessageStatusAsync(command); + + if (response.IsSuccessStatusCode) + { + message.Status = status; + } + else + { + Snackbar.Add($"Failed to update message status: {response.ErrorMessage}", Severity.Error); + } + } + + private void SelectChat(Guid chatId) + { + NavigationManager.NavigateTo($"/chats/{chatId}"); + } + + private string GetChatItemClass(Guid chatId) + { + return ChatId == chatId ? "selected-chat" : string.Empty; + } + + private int GetUnreadMessageCount(Guid chatId) + { + return messages.Count(m => m.ChatId == chatId && m.SenderId != IdentityService.GetCurrentUserId() && m.Status != "Read"); + } + + private string GetMessageBubbleClass(MessageDto message) + { + return message.SenderId == IdentityService.GetCurrentUserId() ? "sent" : "received"; + } + + private string GetSenderName(Guid senderId) + { + return userNames.TryGetValue(senderId, out var name) ? name : "Unknown"; + } + + private string GetSenderImage(Guid senderId) + { + return userImages.TryGetValue(senderId, out var imageUrl) ? imageUrl : "/images/default_profile_image.webp"; + } + + private string GetChatImage(Guid chatId) + { + if (IdentityService.IsAuthenticated) + { + var chat = userChats.FirstOrDefault(c => c.Id == chatId); + + if (chat == null) + { + return "/images/default_profile_image.webp"; + } + + var userId = IdentityService.GetCurrentUserId(); + if (chat.ParticipantIds.Count == 2) + { + var otherParticipantId = chat.ParticipantIds.FirstOrDefault(id => id != userId); + return GetSenderImage(otherParticipantId); + } + + var otherParticipant = chat.ParticipantIds.FirstOrDefault(id => id != userId); + if (otherParticipant != Guid.Empty) + { + return GetSenderImage(otherParticipant); + } + } + + return "/images/default_profile_image.webp"; + } + + private string GetChatName(Guid chatId) + { + var chat = userChats.FirstOrDefault(c => c.Id == chatId); + + if (IdentityService.IsAuthenticated) + { + var userId = IdentityService.GetCurrentUserId(); + if (chat != null && chat.ParticipantIds.Count == 2) + { + var otherParticipantId = chat.ParticipantIds.FirstOrDefault(id => id != userId); + return userNames.TryGetValue(otherParticipantId, out var otherParticipantName) + ? otherParticipantName + : "Unknown Chat"; + } + + return chat?.Name ?? "Unknown Chat"; + } + return "Unknown Chat"; + } + + private async void OnMessageReceived(MessageDto message) + { + await InvokeAsync(() => + { + if (message.ChatId == ChatId) + { + if (!messages.Any(m => m.Id == message.Id)) + { + messages.Add(message); + ScrollToBottomAsync(); + StateHasChanged(); + } + } + + if (lastMessages.ContainsKey(message.ChatId)) + { + lastMessages[message.ChatId] = message; + } + else + { + lastMessages.Add(message.ChatId, message); + } + + StateHasChanged(); + }); + } + + private async void OnMessageStatusUpdated(Guid messageId, string status) + { + await InvokeAsync(() => + { + var message = messages.FirstOrDefault(m => m.Id == messageId); + if (message != null) + { + message.Status = status; + StateHasChanged(); + } + }); + } + + private string GetLastMessagePreview(Guid chatId) + { + if (lastMessages.TryGetValue(chatId, out var lastMessage)) + { + return $"{GetSenderName(lastMessage.SenderId)}: {lastMessage.Content}"; + } + return "No messages yet"; + } + + private string GetLastMessageTime(Guid chatId) + { + if (lastMessages.TryGetValue(chatId, out var lastMessage)) + { + return lastMessage.Timestamp.ToString("g"); + } + return string.Empty; + } + + private bool IsUserTypingInChat(Guid chatId) + { + return typingStatus.TryGetValue(chatId, out var isTyping) && isTyping; + } + + private string GetStatusIcon(string status) + { + return status switch + { + "Sent" => Icons.Material.Filled.Check, + "Delivered" => Icons.Material.Filled.DoneAll, + "Read" => Icons.Material.Filled.Visibility, + _ => Icons.Material.Filled.Schedule // Default icon for pending or unknown status + }; + } + + public async ValueTask DisposeAsync() + { + ChatSignalRService.MessageReceived -= OnMessageReceived; + ChatSignalRService.MessageStatusUpdated -= OnMessageStatusUpdated; + ChatSignalRService.TypingNotificationReceived -= OnTypingNotificationReceived; + ChatSignalRService.ConnectionChanged -= OnConnectionChanged; + await ChatSignalRService.DisposeAsync(); + typingTimer?.Dispose(); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/ChatsAll.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/ChatsAll.razor new file mode 100644 index 000000000..2ebbb0ce6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/ChatsAll.razor @@ -0,0 +1,411 @@ +@page "/chats/all" +@using Astravent.Web.Wasm.HttpClients +@using Astravent.Web.Wasm.Areas.Communication +@using Astravent.Web.Wasm.DTO.Communication +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject ICommunicationService CommunicationService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@inject IDialogService DialogService +@inject ChatSignalRService ChatSignalRService +@using MudBlazor +@using System.Threading +@implements IAsyncDisposable + + + + + + + + @if (filteredChats.Any()) + { + @foreach (var chat in filteredChats) + { + var lastMessage = lastMessages.ContainsKey(chat.Id) ? lastMessages[chat.Id] : null; + var isTyping = typingStatus.ContainsKey(chat.Id); + + + + +
+ @GetChatName(chat.Id) + + @if (isTyping) + { + @typingStatus[chat.Id].UserName is typing... + } + else + { + @GetSenderName(lastMessage?.SenderId ?? Guid.Empty): @lastMessage?.Content + } + +
+ @lastMessage?.Timestamp.ToString("MMM d, h:mm tt") + + Delete Chat + +
+ + } + } + else + { + No chats found. + } +
+
+ + + +@code { + private List userChats = new(); + private Dictionary lastMessages = new(); + private Dictionary userNames = new(); + private Dictionary userImages = new(); + private Dictionary typingStatus = new(); + private string searchQuery = string.Empty; + + private IEnumerable filteredChats => userChats + .Where(chat => string.IsNullOrEmpty(searchQuery) || + GetChatName(chat.Id).Contains(searchQuery, StringComparison.OrdinalIgnoreCase) || + (lastMessages.ContainsKey(chat.Id) && lastMessages[chat.Id]?.Content.Contains(searchQuery, StringComparison.OrdinalIgnoreCase) == true)) + .ToList(); + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await LoadUserChats(); + + var userId = IdentityService.GetCurrentUserId(); + await ChatSignalRService.StartAsync(userId); + ChatSignalRService.MessageReceived += OnMessageReceived; + ChatSignalRService.TypingNotificationReceived += OnTypingNotificationReceived; + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private async Task LoadUserChats() + { + try + { + var userId = IdentityService.GetCurrentUserId(); + var result = await CommunicationService.GetUserChatsAsync(userId, 1, 20); + + if (result != null) + { + userChats = result.Items.SelectMany(u => u.Chats).ToList(); + await LoadLastMessages(); + await LoadUserDetails(); + + userChats = userChats + .OrderByDescending(c => lastMessages.ContainsKey(c.Id) ? lastMessages[c.Id]?.Timestamp : DateTime.MinValue) + .ToList(); + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load chats: {ex.Message}", Severity.Error); + } + } + + private async Task LoadLastMessages() + { + foreach (var chat in userChats) + { + var messages = await CommunicationService.GetMessagesForChatAsync(chat.Id); + lastMessages[chat.Id] = messages.OrderByDescending(m => m.Timestamp).FirstOrDefault(); + } + } + + private async Task LoadUserDetails() + { + var allUserIds = userChats.SelectMany(c => c.ParticipantIds).Distinct().ToList(); + + foreach (var userId in allUserIds) + { + if (!userNames.ContainsKey(userId)) + { + var user = await StudentsService.GetStudentAsync(userId); + if (user != null) + { + userNames[userId] = $"{user.FirstName} {user.LastName}"; + userImages[userId] = string.IsNullOrWhiteSpace(user.ProfileImageUrl) ? "images/default_profile_image.webp" : user.ProfileImageUrl; + } + } + } + } + + private void SelectChat(Guid chatId) + { + NavigationManager.NavigateTo($"/chats/{chatId}"); + } + + private async Task ShowDeleteChatDialog(Guid chatId) + { + var parameters = new DialogParameters + { + ["Message"] = "Are you sure you want to delete this chat? This action cannot be undone." + }; + + var options = new DialogOptions + { + CloseOnEscapeKey = true, + MaxWidth = MaxWidth.Small, + CloseButton = true, + DisableBackdropClick = true, + ClassBackground = "mud-dialog-blur-backdrop" + }; + + var dialog = DialogService.Show("Delete Chat", parameters, options); + + var result = await dialog.Result; + + if (!result.Canceled) + { + await DeleteChat(chatId); + } + } + + private async Task DeleteChat(Guid chatId) + { + try + { + var userId = IdentityService.GetCurrentUserId(); + + await CommunicationService.DeleteChatAsync(chatId, userId); + + userChats = userChats.Where(c => c.Id != chatId).ToList(); + Snackbar.Add("Chat deleted successfully.", Severity.Success); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to delete chat: {ex.Message}", Severity.Error); + } + } + + private string GetChatName(Guid chatId) + { + var chat = userChats.FirstOrDefault(c => c.Id == chatId); + + if (chat != null && chat.ParticipantIds.Count == 2) + { + var otherParticipantId = chat.ParticipantIds.FirstOrDefault(id => id != IdentityService.GetCurrentUserId()); + return userNames.TryGetValue(otherParticipantId, out var otherParticipantName) ? otherParticipantName : "Unknown Chat"; + } + + return chat?.Name ?? "Group Chat"; + } + + private string GetChatImage(Guid chatId) + { + var chat = userChats.FirstOrDefault(c => c.Id == chatId); + if (chat == null) return "/images/default_profile_image.webp"; + + var userId = IdentityService.GetCurrentUserId(); + if (chat.ParticipantIds.Count == 2) + { + var otherParticipantId = chat.ParticipantIds.FirstOrDefault(id => id != userId); + return GetSenderImage(otherParticipantId); + } + + return "/images/default_profile_image.webp"; + } + + private string GetSenderImage(Guid senderId) + { + return userImages.TryGetValue(senderId, out var imageUrl) ? imageUrl : "/images/default_profile_image.webp"; + } + + private string GetSenderName(Guid senderId) + { + return userNames.TryGetValue(senderId, out var name) ? name : "Unknown"; + } + + private async void OnMessageReceived(MessageDto message) + { + await InvokeAsync(async () => + { + // Update last message in chat + lastMessages[message.ChatId] = message; + + // Move chat to the top of the list + var chat = userChats.FirstOrDefault(c => c.Id == message.ChatId); + if (chat != null) + { + userChats.Remove(chat); + userChats.Insert(0, chat); + } + else + { + // Load chat if not found (e.g., was deleted locally) + await LoadUserChats(); + } + + StateHasChanged(); + }); + } + + private async void OnTypingNotificationReceived(string userId, bool isTyping) + { + await InvokeAsync(() => + { + var parsedUserId = Guid.Parse(userId); + var chatId = userChats.FirstOrDefault(c => c.ParticipantIds.Contains(parsedUserId))?.Id ?? Guid.Empty; + + if (chatId != Guid.Empty) + { + if (isTyping) + { + if (userNames.TryGetValue(parsedUserId, out var typingUserName)) + { + typingStatus[chatId] = (typingUserName, new Timer(OnTypingTimeout, chatId, 1000, Timeout.Infinite)); + } + } + else + { + if (typingStatus.TryGetValue(chatId, out var typingEntry)) + { + typingEntry.Timer.Dispose(); + typingStatus.Remove(chatId); + } + } + + StateHasChanged(); + } + }); + } + + private void OnTypingTimeout(object state) + { + if (state is Guid chatId && typingStatus.TryGetValue(chatId, out var typingEntry)) + { + typingEntry.Timer.Dispose(); + typingStatus.Remove(chatId); + + InvokeAsync(() => StateHasChanged()); + } + } + + public async ValueTask DisposeAsync() + { + foreach (var (_, timer) in typingStatus.Values) + { + timer.Dispose(); + } + + ChatSignalRService.MessageReceived -= OnMessageReceived; + ChatSignalRService.TypingNotificationReceived -= OnTypingNotificationReceived; + await ChatSignalRService.DisposeAsync(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/DeleteChatDialog.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/DeleteChatDialog.razor new file mode 100644 index 000000000..339f00394 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/DeleteChatDialog.razor @@ -0,0 +1,19 @@ +@using MudBlazor + + + + @Message + + + Cancel + Delete + + + +@code { + [CascadingParameter] MudDialogInstance MudDialog { get; set; } + [Parameter] public string Message { get; set; } + + private void DeleteChat() => MudDialog.Close(DialogResult.Ok(true)); + private void Cancel() => MudDialog.Cancel(); +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/NewChat.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/NewChat.razor new file mode 100644 index 000000000..fc35ec323 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Chats/NewChat.razor @@ -0,0 +1,302 @@ +@page "/chats/new" +@using Astravent.Web.Wasm.HttpClients +@using Astravent.Web.Wasm.Areas.Friends +@using Astravent.Web.Wasm.DTO +@inject IIdentityService IdentityService +@inject IFriendsService FriendsService +@inject ICommunicationService CommunicationService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using MudBlazor + + + + + +
+
+ + + @if (!pageInitialized) + { +
+ +
+ } + else if (friends != null && friends.Any()) + { + foreach (var friend in friends) + { +
+
+ Friend Image +
+
@friend.StudentDetails.FirstName @friend.StudentDetails.LastName
+

@friend.StudentDetails.Email

+
+
+ + + Start Chat + +
+
+
+ } + + @if (hasMorePages) + { + + + Load More + + } + } + else + { +

No friends to show. Start connecting now!

+ } +
+
+
+
+ + + +@code { + private List friends = new List(); + private string searchTerm; + private bool pageInitialized; + private int currentPage = 1; + private int pageSize = 10; + private int totalFriends; + private bool hasMorePages => friends.Count < totalFriends; + + + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Chats", href: "/chats", icon: Icons.Material.Filled.Chat), + new BreadcrumbItem("New Chat", href: "/chats/new", disabled: true, icon: Icons.Material.Filled.AddComment), + }; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + await LoadFriends(); + pageInitialized = true; + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private string GetProfileImageUrl(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "images/default_profile_image.webp" : profileImageUrl; + } + + private async Task StartChatWithFriend(Guid friendId, string friendName) + { + try + { + // Check if the chat already exists + var userId = IdentityService.GetCurrentUserId(); + var existingChat = await CommunicationService.FindExistingChatAsync(userId, friendId); + + if (existingChat != null) + { + // If chat exists, navigate to it + Console.WriteLine("Navigating to existing chat..."); + NavigationManager.NavigateTo($"/chats/{existingChat.Id}"); + } + else + { + // If no chat exists, create a new one + var command = new CreateChatCommand( + chatId: Guid.NewGuid(), + participantIds: new List { userId, friendId }, + chatName: $"Chat with {friendName}" + ); + + var response = await CommunicationService.CreateChatAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add($"You have started a chat with {friendName}.", Severity.Success); + NavigationManager.NavigateTo($"/chats/{response.Content}"); + } + else + { + Snackbar.Add("An error occurred while creating the chat.", Severity.Error); + } + } + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred: {ex.Message}", Severity.Error); + } + } + + private void SearchFriends() + { + if (!string.IsNullOrWhiteSpace(searchTerm)) + { + searchTerm = searchTerm.Trim(); + friends = friends.Where(f => f.StudentDetails.FirstName.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) || f.StudentDetails.LastName.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)).ToList(); + } + else + { + ReloadFriends().Wait(); + } + StateHasChanged(); + } + + private async Task ReloadFriends() + { + currentPage = 1; + friends.Clear(); + await LoadFriends(); + } + + private async Task LoadFriends() + { + try + { + var studentId = IdentityService.GetCurrentUserId(); + var result = await FriendsService.GetAllFriendsAsync(studentId, currentPage, pageSize); + if (result != null) + { + friends.AddRange(result.Items); + totalFriends = result.TotalItems; + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to Load Friends: {ex.Message}", Severity.Error); + } + } + + private async Task LoadMoreFriends() + { + currentPage++; + await LoadFriends(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Connect.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Connect.razor new file mode 100644 index 000000000..7c376710a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Connect.razor @@ -0,0 +1,57 @@ +@using MudBlazor +@inject NavigationManager NavigationManager + +
+ + + + Connect with Your World + + + Join MiniSpace today and start exploring, sharing, and connecting like never before. Create your account or login to dive into the experience. + +
+ + Create Account + + + Login + +
+
+
+ +@code { + private void GoToRegister() + { + NavigationManager.NavigateTo("/signup"); + } + + private void GoToLogin() + { + NavigationManager.NavigateTo("/signin"); + } +} + + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Error.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Error.razor new file mode 100644 index 000000000..79929d783 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Error.razor @@ -0,0 +1,16 @@ +@page "/error" + + +

Error.

+

An error occurred while processing your request.

+ +

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. +

\ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/AllEvents.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/AllEvents.razor new file mode 100644 index 000000000..4003addde --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/AllEvents.razor @@ -0,0 +1,281 @@ +@page "/events/search" +@inject IEventsService EventsService +@inject ISnackbar Snackbar +@inject NavigationManager NavigationManager +@inject IIdentityService IdentityService +@using MudBlazor +@using Astravent.Web.Wasm.Areas.Events.CommandsDto +@using Astravent.Web.Wasm.DTO.Wrappers +@using Astravent.Web.Wasm.DTO.Events + + + + Explore Events + + + + + + + + All + @foreach (var category in Enum.GetValues(typeof(Category)).Cast()) + { + @category + } + + + + + + + + + + + + + + + + Search + + + + + @if (events?.Any() ?? false) + { + @foreach (var eventDto in events) + { + + + + + @eventDto.Name + @eventDto.Category + Starts: @eventDto.StartDate.ToString("MMMM dd, yyyy") + Ends: @eventDto.EndDate.ToString("MMMM dd, yyyy") + + + Interested: @eventDto.InterestedStudents + + + + Signed Up: @eventDto.SignedUpStudents + + + + + + @* View Details *@ + + @if (eventDto.IsSignedUp) + { + + + Signed Up + + } + else + { + + + Sign Up + + } + @if (eventDto.IsInterested) + { + + + Interested + + } + else + { + + + Show Interest + + } + + + + } + } + else + { + No events found. + } + + + + + + + + + + +@code { + private List events = new(); + private int totalItems; + private int currentPage = 1; + private int pageSize = 9; + private string searchQuery = string.Empty; + private string selectedCategory = null; + private decimal? minPrice; + private decimal? maxPrice; + private DateTime? startDate; + private DateTime? endDate; + private readonly int[] pageSizeOptions = { 6, 9, 12 }; + private bool _isAuthenticated; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + + _isAuthenticated = IdentityService.IsAuthenticated; + if (_isAuthenticated) + { + await LoadEvents(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + + private async Task LoadEvents() + { + try + { + if (string.IsNullOrWhiteSpace(searchQuery) && selectedCategory == null && !minPrice.HasValue && !maxPrice.HasValue && !startDate.HasValue && !endDate.HasValue) + { + var paginatedResult = await EventsService.GetPaginatedEventsAsync(currentPage, pageSize); + if (paginatedResult != null) + { + events = paginatedResult.Items.ToList(); + totalItems = paginatedResult.TotalItems; + } + else + { + events = new List(); + totalItems = 0; + Snackbar.Add("No events found or failed to load events.", Severity.Warning); + } + } + else + { + var command = new SearchEvents + { + Name = searchQuery, + Category = selectedCategory, + DateFrom = startDate?.ToString("yyyy-MM-dd"), + DateTo = endDate?.ToString("yyyy-MM-dd"), + Pageable = new PageableDto + { + Page = currentPage, + Size = pageSize + } + }; + + var pagedResult = await EventsService.SearchEventsAsync(command); + if (pagedResult != null) + { + events = pagedResult.Items.ToList(); + totalItems = pagedResult.TotalItems; + } + else + { + events = new List(); + totalItems = 0; + Snackbar.Add("No events found or failed to load events.", Severity.Warning); + } + } + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred: {ex.Message}", Severity.Error); + } + } + + + private async Task PerformSearch() + { + currentPage = 1; + await LoadEvents(); + } + + private async Task OnPageChanged(int page) + { + currentPage = page; + await LoadEvents(); + } + + private void ViewEvent(Guid eventId) + { + NavigationManager.NavigateTo($"/events/event/{eventId}"); + } + + private async Task SignUpToEvent(Guid eventId) + { + if (!_isAuthenticated) + { + Snackbar.Add("Please sign in to sign up for events.", Severity.Warning); + NavigationManager.NavigateTo("/signin"); + return; + } + + try + { + var command = new SignUpToEventCommand + { + EventId = eventId, + StudentId = IdentityService.GetCurrentUserId() + }; + + await EventsService.SignUpToEventAsync(command); + Snackbar.Add("Successfully signed up for the event.", Severity.Success); + await LoadEvents(); // Refresh events to reflect the updated status + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred while signing up: {ex.Message}", Severity.Error); + } + } + + private async Task ShowInterestInEvent(Guid eventId) + { + if (!_isAuthenticated) + { + Snackbar.Add("Please sign in to show interest in events.", Severity.Warning); + NavigationManager.NavigateTo("/signin"); + return; + } + + try + { + var command = new ShowInterestInEventCommand + { + EventId = eventId, + StudentId = IdentityService.GetCurrentUserId() + }; + + await EventsService.ShowInterestInEventAsync(command); + Snackbar.Add("Your interest in the event has been noted.", Severity.Info); + await LoadEvents(); // Refresh events to reflect the updated status + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred while showing interest: {ex.Message}", Severity.Error); + } + } + + private string GetBannerUrl(EventDto eventDto) + { + return eventDto != null && !string.IsNullOrWhiteSpace(eventDto.BannerUrl) ? eventDto.BannerUrl : "/images/default_media_file_image.png"; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/EventDetails.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/EventDetails.razor new file mode 100644 index 000000000..6921cfdc8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/EventDetails.razor @@ -0,0 +1,201 @@ +@page "/events/event/{eventId:guid}" +@inject IEventsService EventsService +@inject IStudentsService StudentsService +@inject IOrganizationsService OrganizationsService +@inject ISnackbar Snackbar +@inject NavigationManager NavigationManager +@inject IIdentityService IdentityService +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Events +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.Events.CommandsDto +@using Astravent.Web.Wasm.DTO.Enums + + + @if (eventDetails != null) + { + + + + @eventDetails.Name + + Organized by: @organizerName + + @eventDetails.Description + + Details + Category: @eventDetails.Category + Starts: @eventDetails.StartDate.ToString("MMMM dd, yyyy h:mm tt") + Ends: @eventDetails.EndDate.ToString("MMMM dd, yyyy h:mm tt") + Location: @FormatAddress(eventDetails.Location) + + + + Interested: @eventDetails.InterestedStudents + + + + Signed Up: @eventDetails.SignedUpStudents + + + @if (eventDetails.StudentRating.HasValue) + { + + } + + + @if (eventDetails.IsSignedUp) + { + + Signed Up + + } + else + { + + Sign Up + + } + @if (eventDetails.IsInterested) + { + + Interested + + } + else + { + + Show Interest + + } + + + } + else + { + Loading event details... + } + + + + +@code { + [Parameter] public Guid eventId { get; set; } + private EventDto eventDetails; + private string organizerName; + + protected override async Task OnInitializedAsync() + { + await LoadEventDetails(); + } + + private async Task LoadEventDetails() + { + try + { + eventDetails = await EventsService.GetEventAsync(eventId); + if (eventDetails == null) + { + Snackbar.Add("Event not found.", Severity.Error); + NavigationManager.NavigateTo("/events/search"); + return; + } + + organizerName = await GetOrganizerName(eventDetails.Organizer); + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred: {ex.Message}", Severity.Error); + NavigationManager.NavigateTo("/events/search"); + } + } + + private async Task GetOrganizerName(OrganizerDto organizer) + { + try + { + if (organizer.OrganizerType == OrganizerType.User && organizer.UserId.HasValue) + { + var user = await StudentsService.GetStudentAsync(organizer.UserId.Value); + return $"{user.FirstName} {user.LastName}"; + } + else if (organizer.OrganizerType == OrganizerType.Organization && organizer.OrganizationId.HasValue) + { + var organization = await OrganizationsService.GetOrganizationAsync(organizer.OrganizationId.Value); + return organization.Name; + } + return "Unknown Organizer"; + } + catch (Exception ex) + { + Snackbar.Add($"Error loading organizer details: {ex.Message}", Severity.Error); + return "Unknown Organizer"; + } + } + + private string FormatAddress(AddressDto address) + { + // Format the address as a single string + return $"{address.Street}, {address.City}, {address.Country}, {address.ZipCode}"; + } + + private async Task SignUpToEvent(Guid eventId) + { + if (!IdentityService.IsAuthenticated) + { + Snackbar.Add("Please sign in to sign up for events.", Severity.Warning); + NavigationManager.NavigateTo("/signin"); + return; + } + + try + { + var command = new SignUpToEventCommand + { + EventId = eventId, + StudentId = IdentityService.GetCurrentUserId() + }; + + await EventsService.SignUpToEventAsync(command); + Snackbar.Add("Successfully signed up for the event.", Severity.Success); + await LoadEventDetails(); // Refresh the event details to reflect the updated status + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred while signing up: {ex.Message}", Severity.Error); + } + } + + private async Task ShowInterestInEvent(Guid eventId) + { + if (!IdentityService.IsAuthenticated) + { + Snackbar.Add("Please sign in to show interest in events.", Severity.Warning); + NavigationManager.NavigateTo("/signin"); + return; + } + + try + { + var command = new ShowInterestInEventCommand + { + EventId = eventId, + StudentId = IdentityService.GetCurrentUserId() + }; + + await EventsService.ShowInterestInEventAsync(command); + Snackbar.Add("Your interest in the event has been noted.", Severity.Info); + await LoadEventDetails(); // Refresh the event details to reflect the updated status + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred while showing interest: {ex.Message}", Severity.Error); + } + } + + private string GetBannerUrl(EventDto eventDto) + { + return eventDto != null && !string.IsNullOrWhiteSpace(eventDto.BannerUrl) ? eventDto.BannerUrl : "/images/default_media_file_image.png"; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/EventsFollowing.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/EventsFollowing.razor new file mode 100644 index 000000000..5440aeff9 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/EventsFollowing.razor @@ -0,0 +1,210 @@ +@page "/events/following" +@inject IEventsService EventsService +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Events +@using System.Threading.Tasks +@using Astravent.Web.Wasm.Areas.Events.CommandsDto + + + + Your Events + + + + + @if (signedUpEvents?.Any() ?? false) + { + @foreach (var eventDto in signedUpEvents) + { + + + + + @eventDto.Name + @eventDto.Category + Starts: @eventDto.StartDate.ToString("MMMM dd, yyyy") + Ends: @eventDto.EndDate.ToString("MMMM dd, yyyy") + + + Signed Up: @eventDto.SignedUpStudents + + + + Interested: @eventDto.InterestedStudents + + + + + + View Details + + + + Signed Up + + + + + } + } + else + { + You haven't signed up for any events yet. + } + + + + + + @if (interestedEvents?.Any() ?? false) + { + @foreach (var eventDto in interestedEvents) + { + + + + + @eventDto.Name + @eventDto.Category + Starts: @eventDto.StartDate.ToString("MMMM dd, yyyy") + Ends: @eventDto.EndDate.ToString("MMMM dd, yyyy") + + + Signed Up: @eventDto.SignedUpStudents + + + + Interested: @eventDto.InterestedStudents + + + + + + View Details + + + + Interested + + + + + } + } + else + { + You haven't marked interest in any events yet. + } + + + + + + +@code { + private List signedUpEvents = new(); + private List interestedEvents = new(); + private bool _isAuthenticated; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + + _isAuthenticated = IdentityService.IsAuthenticated; + if (_isAuthenticated) + { + await LoadUserEvents(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + + private async Task LoadUserEvents() + { + try + { + var userId = IdentityService.GetCurrentUserId(); + + // Load signed-up events + var signedUpEventsResult = await EventsService.GetUserEventsAsync(userId, 1, int.MaxValue, "SignedUp"); + signedUpEvents = signedUpEventsResult.Items.ToList(); + + // Load interested events + var interestedEventsResult = await EventsService.GetUserEventsAsync(userId, 1, int.MaxValue, "InterestedIn"); + interestedEvents = interestedEventsResult.Items.ToList(); + } + catch (Exception ex) + { + Console.WriteLine($"Error loading user events: {ex.Message}"); + } + } + + private void ViewEvent(Guid eventId) + { + NavigationManager.NavigateTo($"/events/event/{eventId}"); + } + + private async Task SignUpToEvent(Guid eventId) + { + if (!_isAuthenticated) + { + Snackbar.Add("Please sign in to sign up for events.", Severity.Warning); + NavigationManager.NavigateTo("/signin"); + return; + } + + try + { + var command = new SignUpToEventCommand + { + EventId = eventId, + StudentId = IdentityService.GetCurrentUserId() + }; + + await EventsService.SignUpToEventAsync(command); + Snackbar.Add("Successfully signed up for the event.", Severity.Success); + await LoadUserEvents(); // Refresh events to reflect the updated status + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred while signing up: {ex.Message}", Severity.Error); + } + } + + private async Task ShowInterestInEvent(Guid eventId) + { + if (!_isAuthenticated) + { + Snackbar.Add("Please sign in to show interest in events.", Severity.Warning); + NavigationManager.NavigateTo("/signin"); + return; + } + + try + { + var command = new ShowInterestInEventCommand + { + EventId = eventId, + StudentId = IdentityService.GetCurrentUserId() + }; + + await EventsService.ShowInterestInEventAsync(command); + Snackbar.Add("Your interest in the event has been noted.", Severity.Info); + await LoadUserEvents(); // Refresh events to reflect the updated status + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred while showing interest: {ex.Message}", Severity.Error); + } + } + + private string GetBannerUrl(EventDto eventDto) + { + return eventDto != null && !string.IsNullOrWhiteSpace(eventDto.BannerUrl) ? eventDto.BannerUrl : "/images/default_media_file_image.png"; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/MyEvents.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/MyEvents.razor new file mode 100644 index 000000000..36f114f46 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/MyEvents.razor @@ -0,0 +1,94 @@ +@page "/events/my-events" +@inject IEventsService EventsService +@inject IIdentityService IdentityService +@using MudBlazor +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar + + + My Events + + + @if (events?.Any() ?? false) + { + @foreach (var eventDto in events) + { + + + + + @eventDto.Name + @eventDto.Category + Starts: @eventDto.StartDate.ToString("MMMM dd, yyyy") + Ends: @eventDto.EndDate.ToString("MMMM dd, yyyy") + + + Signed Up: @eventDto.SignedUpStudents + + + + Interested: @eventDto.InterestedStudents + + + + + View Details + + + + + } + } + else + { + No events found. + } + + + + + + + + + +@code { + private List events = new(); + private int totalItems; + private int currentPage = 1; + private int pageSize = 9; + private readonly int[] pageSizeOptions = { 6, 9, 12 }; + + protected override async Task OnInitializedAsync() + { + await LoadEvents(); + } + + private async Task LoadEvents() + { + try + { + var organizerId = IdentityService.GetCurrentUserId(); + var response = await EventsService.GetMyEventsAsync(organizerId, currentPage, pageSize); + + events = response.Items.ToList(); + totalItems = response.TotalItems; + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred: {ex.Message}", Severity.Error); + } + } + + private async Task OnPageChanged(int page) + { + currentPage = page; + await LoadEvents(); + } + + private void ViewEvent(Guid eventId) + { + NavigationManager.NavigateTo($"/events/event/{eventId}"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/OrganizeEvents.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/OrganizeEvents.razor new file mode 100644 index 000000000..46a44137a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Events/OrganizeEvents.razor @@ -0,0 +1,513 @@ +@page "/events/organize" +@using Astravent.Web.Wasm.Areas.Events +@using Astravent.Web.Wasm.Areas.Events.CommandsDto +@using Astravent.Web.Wasm.DTO.Enums +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Utilities +@inject IEventsService EventsService +@inject ISnackbar Snackbar +@inject IMediaFilesService MediaFilesService +@inject IIdentityService IdentityService +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager +@using MudBlazor +@using System.IO +@using System.Text.Json; + + + Organize Event + + @if (IsUploading) + { +
+ + Uploading Files, please wait... +
+ } + else + { + + + + + + General Information + + + Event Description + + + + + + + + + + + + + + @foreach (var category in Enum.GetNames(typeof(Category))) + { + @category + } + + + + @foreach (var organizerType in Enum.GetNames(typeof(OrganizerType))) + { + @organizerType + } + + + + + + + + + + + @if (IsLocationDefined) + { + + Location + + + + + + + + } + + + + + Payment + Requires Payment + + @if (newEvent.Settings.RequiresPayment) + { + + + @foreach (var method in Enum.GetNames(typeof(PaymentMethod))) + { + @method + } + + + + + + } + + + + + Media and Visibility + + + + + + + + Additional Settings + Requires Approval + Is Online Event + Is Private + Requires RSVP + Allows Guests + Show Attendees Publicly + Send Reminders + + Enable Chat + Allow Comments + Issue Tickets + Record Event + + + + + + + Submit + Create Event + + + + } +
+ + + + + + +@code { + private CreateEventCommand newEvent = new CreateEventCommand(); + private MudForm form; + + private string bannerPreviewUrl; + private List mediaFilesPreviews = new(); + private string CroppedImageBase64; + + private IBrowserFile croppedImageFile; + private IReadOnlyList mediaFiles; + private bool IsUploading { get; set; } = false; + private string currentImageType = string.Empty; + + private string defaultBannerImage = "/images/default_banner_image.png"; + private string defaultMediaFileImage = "/images/default_media_file_image.png"; + + private DateTime? startDateTemp; + private TimeSpan? startTimeTemp; + private DateTime? endDateTemp; + private TimeSpan? endTimeTemp; + private DateTime? publishDateTemp; + private TimeSpan? publishTimeTemp; + private bool IsLoading { get; set; } = true; + private bool IsLocationDefined { get; set; } = false; + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + newEvent.OrganizerId = IdentityService.GetCurrentUserId(); + newEvent.OrganizerType = "User"; // OrganizerType is set to "User" by default + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add($"Failed to initialize authentication state: {ex.Message}", Severity.Error); + } + finally + { + IsLoading = false; + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", DotNetObjectReference.Create(this)); + } + } + + private async Task UploadBannerClick() + { + // Ensure the element is available in the DOM before triggering the click + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputBanner').click()"); + } + + private async Task UploadMediaFilesClick() + { + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputMediaFiles').click()"); + } + + private async Task OpenCropper(InputFileChangeEventArgs e, string imageType) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageType = imageType; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, imageType); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); + + try + { + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + string imageType = "EventBanner"; + var response = await MediaFilesService.UploadMediaFileAsync( + sourceId: newEvent.EventId, + sourceType: imageType, + uploaderId: IdentityService.GetCurrentUserId(), + fileName: $"{newEvent.Name}_{currentImageType}.png", + fileContentType: croppedImageFile.ContentType, + fileData: fileData + ); + + var responseDto = response?.Content; + + if (response.IsSuccessStatusCode && responseDto != null) + { + var uploadedUrl = responseDto.ProcessedUrl; + if (!string.IsNullOrWhiteSpace(uploadedUrl)) + { + bannerPreviewUrl = uploadedUrl; + newEvent.BannerUrl = bannerPreviewUrl; + StateHasChanged(); + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Uploaded URL is null or empty."); + } + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Failed to upload the image."); + } + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + finally + { + IsUploading = false; + StateHasChanged(); + } + + CloseCropper(); + } + } + + private async Task UploadMediaFiles(InputFileChangeEventArgs e) + { + const long maxAllowedSize = 10 * 1024 * 1024; + mediaFiles = e.GetMultipleFiles(); + + foreach (var file in mediaFiles) + { + if (file.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + } + + foreach (var file in mediaFiles) + { + using var stream = file.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + + var response = await MediaFilesService.UploadFileAsync( + sourceId: newEvent.EventId, + sourceType: "EventFile", + uploaderId: IdentityService.GetCurrentUserId(), + fileName: file.Name, + fileContentType: file.ContentType, + fileData: buffer + ); + + var responseDto = response.Content; + + if (response.IsSuccessStatusCode && responseDto != null) + { + var uploadedUrl = responseDto.FileUrl; + if (!string.IsNullOrWhiteSpace(uploadedUrl)) + { + mediaFilesPreviews.Add(new FilePreview + { + Url = uploadedUrl, + ContentType = file.ContentType, + IsImage = file.ContentType.StartsWith("image/"), + IsVideo = file.ContentType.StartsWith("video/"), + IsPdf = file.ContentType.EndsWith("pdf") + }); + ((List)newEvent.MediaFilesUrl).Add(uploadedUrl); + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Uploaded URL is null or empty."); + } + } + else + { + await JSRuntime.InvokeVoidAsync("alert", $"Failed to upload {file.Name}."); + } + } + + StateHasChanged(); + } + + private async Task CreateEvent() + { + newEvent.OrganizerId = IdentityService.GetCurrentUserId(); + + if (startDateTemp.HasValue && startTimeTemp.HasValue) + { + newEvent.StartDate = (startDateTemp.Value.Date + startTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (endDateTemp.HasValue && endTimeTemp.HasValue) + { + newEvent.EndDate = (endDateTemp.Value.Date + endTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (publishDateTemp.HasValue && publishTimeTemp.HasValue) + { + newEvent.PublishDate = (publishDateTemp.Value.Date + publishTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (!IsLocationDefined) + { + newEvent.BuildingName = null; + newEvent.Street = null; + newEvent.BuildingNumber = null; + newEvent.ApartmentNumber = null; + newEvent.City = null; + newEvent.ZipCode = null; + newEvent.Country = null; + } + + await form.Validate(); + if (form.IsValid) + { + var formDataJson = JsonSerializer.Serialize(newEvent, new JsonSerializerOptions { WriteIndented = true }); + Console.WriteLine("Form data to be sent:"); + Console.WriteLine(formDataJson); + + var response = await EventsService.CreateEventAsync(newEvent); + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Event created successfully!", Severity.Success); + } + else + { + Snackbar.Add("Failed to create event. Please try again.", Severity.Error); + } + } + } + + private string GetBannerUrl() + { + return !string.IsNullOrWhiteSpace(bannerPreviewUrl) ? bannerPreviewUrl : defaultBannerImage; + } + + private class FilePreview + { + public string Url { get; set; } + public string ContentType { get; set; } + public bool IsImage { get; set; } + public bool IsVideo { get; set; } + public bool IsPdf { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/Home.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/Home.razor new file mode 100644 index 000000000..b59358bce --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/Home.razor @@ -0,0 +1,250 @@ +@page "/home" +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Wrappers +@using Astravent.Web.Wasm.Pages.Posts.Components +@inject NavigationManager NavigationManager +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject IPostsService PostsService +@inject IReactionsService ReactionsService +@inject ICommentsService CommentsService +@inject ISnackbar Snackbar +@using MudBlazor +@inject IIdentityService IdentityService +@inject AuthenticationStateProvider AuthenticationStateProvider + + + + @if (!pageInitialized) + { +
+ + Loading, please wait... +
+ } + else + { + + + + + + + + + + @if (postsLoadingFailed) + { + Failed to load posts. Please try again later. + } + else if (posts != null && posts.Any()) + { + + @foreach (var post in posts) + { + + + + } + + } + else + { + No activity found + Please join an event first + } + + + + + + + + } +
+
+ +@code { + private IEnumerable posts; + private Guid studentId; + private bool pageInitialized = false; + private bool postsLoadingFailed = false; + private Dictionary reactionsSummaries = new Dictionary(); + + // Cache for user names and avatars + private Dictionary userNames = new Dictionary(); + private Dictionary userAvatars = new Dictionary(); + + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + }; + + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + await LoadPostsAsync(); + + var user = authState.User; + + if (user.Identity.IsAuthenticated) + { + Console.WriteLine($"User authenticated for the home component: {user.Identity.IsAuthenticated}"); + studentId = await IdentityService.GetCurrentUserIdFromJwtAsync(); + + pageInitialized = true; + } + else + { + NavigationManager.NavigateTo("/signin"); + } + } + + private async Task LoadPostsAsync() + { + + Console.WriteLine("Loading posts... async"); + var result = await PostsService.GetCurrentUserFeedAsync(1, 8, "PublishDate", "desc"); + Console.WriteLine($"Posts loaded: {result.IsSuccessStatusCode}"); + if (result.IsSuccessStatusCode) + { + posts = result.Content.Items; + var postIds = posts.Select(p => p.Id).ToList(); + reactionsSummaries = await ReactionsService.GetReactionsSummariesAsync(postIds, ReactionContentType.Post); + + var userIds = posts.Select(p => p.UserId).Where(id => id.HasValue).Select(id => id.Value).Distinct().ToList(); + foreach (var userId in userIds) + { + if (!userNames.ContainsKey(userId)) + { + var user = await StudentsService.GetStudentAsync(userId); + if (user != null) + { + userAvatars[userId] = user.ProfileImageUrl ?? string.Empty; + } + } + } + } + else + { + postsLoadingFailed = true; + Console.WriteLine($"Error loading posts: {result.ErrorMessage.Reason}"); + Snackbar.Add($"Error loading posts: {result.ErrorMessage.Reason}", Severity.Error); + } + + } + // Method to get cached user name + private string GetCachedUserName(Guid? userId) + { + if (userId.HasValue && userNames.ContainsKey(userId.Value)) + { + return userNames[userId.Value]; + } + return "Unknown User"; + } + + // Method to get cached user avatar + private string GetCachedUserAvatar(Guid? userId) + { + if (userId.HasValue && userAvatars.ContainsKey(userId.Value)) + { + return userAvatars[userId.Value]; + } + return string.Empty; + } + + private async Task HandleReaction((Guid postId, ReactionType reactionType) reactionInfo) + { + var (postId, reactionType) = reactionInfo; + + var reactions = await ReactionsService.GetReactionsAsync(postId, ReactionContentType.Post); + var existingReaction = reactions.FirstOrDefault(r => r.UserId == IdentityService.GetCurrentUserId()); + + if (existingReaction != null) + { + var updateReaction = new UpdateReactionDto + { + ReactionId = existingReaction.Id, + UserId = IdentityService.GetCurrentUserId(), + NewReactionType = reactionType.ToString(), + ContentType = "Post", + TargetType = "User" + }; + + await ReactionsService.UpdateReactionAsync(updateReaction); + } + else + { + var createReaction = new CreateReactionDto + { + UserId = IdentityService.GetCurrentUserId(), + ContentId = postId, + ContentType = "Post", + ReactionType = reactionType.ToString(), + TargetType = "User" + }; + + await ReactionsService.CreateReactionAsync(createReaction); + } + + reactionsSummaries[postId] = await ReactionsService.GetReactionsSummaryAsync(postId, ReactionContentType.Post); + } + + private async Task SubmitCommentAsync((PostDto post, string commentText) commentInfo) + { + var (post, commentText) = commentInfo; + if (string.IsNullOrWhiteSpace(commentText)) + { + Snackbar.Add("Comment cannot be empty.", Severity.Warning); + return; + } + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: DetermineCommentContext(post).ToString(), + userId: IdentityService.GetCurrentUserId(), + parentId: Guid.Empty, + textContent: commentText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment added successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to add comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private void NavigateToPostDetails(Guid postId) + { + NavigationManager.NavigateTo($"/posts/details/{postId}"); + } + + private CommentContext DetermineCommentContext(PostDto post) + { + if (post.OrganizationId.HasValue) + { + return post.EventId.HasValue ? CommentContext.OrganizationEvent : CommentContext.OrganizationPost; + } + else + { + return post.EventId.HasValue ? CommentContext.UserEvent : CommentContext.UserPost; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/PostCard.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/PostCard.razor new file mode 100644 index 000000000..d594aece6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/PostCard.razor @@ -0,0 +1,435 @@ +@using Astravent.Web.Wasm.DTO +@using MudBlazor +@inject IReactionsService ReactionsService +@inject ICommentsService CommentsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager + + + + + + + + + + @GetUserName(Post.UserId) + @Post.CreatedAt.ToString("g") + + + + + + @if (Post.MediaFiles != null && Post.MediaFiles.Any()) + { + + } + + @if (ReactionsSummary != null) + { + + @foreach (var reaction in ReactionsSummary.ReactionsWithCounts.OrderByDescending(r => r.Value)) + { + + + @reaction.Value + + } + + Total: @ReactionsSummary.NumberOfReactions + + + } + + + + + + + View + + + + + + React + + + + @foreach (var reactionType in Enum.GetValues(typeof(ReactionType)).Cast()) + { + + @reactionType.GetReactionText() + + } + + + + + Comment + + + + @if (IsCommentSectionVisible) + { + + + Submit + + @if (Comments != null && Comments.Any()) + { + + @foreach (var comment in Comments) + { + + + + + + + @GetUserName(comment.UserId) + @comment.TextContent + @comment.CreatedAt.ToString("g") + + + @($"{comment.Likes?.Count() ?? 0} people liked this comment") + + + + Like + + + + Reply + + + @if (comment.Id == activeReplyCommentId) + { + + Submit Reply + } + + @if (comment.Replies != null && comment.Replies.Any()) + { + + @foreach (var reply in comment.Replies) + { + + + + + + + @GetUserName(reply.UserId) + @reply.TextContent + @reply.CreatedAt.ToString("g") + + + + + Like + + + + + } + + } + + + + } + + } + else + { + No comments available. + } + + } + + +@code { + [Parameter] + public PostDto Post { get; set; } + + private ReactionsSummaryDto ReactionsSummary { get; set; } + private List Comments { get; set; } = new(); + private bool IsCommentSectionVisible { get; set; } = false; + private string NewCommentText { get; set; } = string.Empty; + private string newReplyText { get; set; } = string.Empty; + private Guid? activeReplyCommentId { get; set; } = null; + + private Dictionary studentsCache = new(); + + protected override async Task OnInitializedAsync() + { + ReactionsSummary = await ReactionsService.GetReactionsSummaryAsync(Post.Id, ReactionContentType.Post); + Comments = await LoadCommentsForPostAsync(); + + if (Post.UserId.HasValue && !studentsCache.ContainsKey(Post.UserId.Value)) + { + var student = await StudentsService.GetStudentAsync(Post.UserId.Value); + if (student != null) + { + studentsCache[Post.UserId.Value] = student; + } + } + } + + private async Task> LoadCommentsForPostAsync() + { + var command = new SearchRootCommentsCommand( + contextId: Post.Id, + commentContext: DetermineCommentContext().ToString(), + pageable: new PageableDto + { + Page = 1, + Size = 10, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "asc" + } + } + ); + + var response = await CommentsService.SearchRootCommentsAsync(command); + var comments = response.Items?.ToList() ?? new List(); + + foreach (var comment in comments) + { + if (!studentsCache.ContainsKey(comment.UserId)) + { + var student = await StudentsService.GetStudentAsync(comment.UserId); + if (student != null) + { + studentsCache[comment.UserId] = student; + } + } + + if (comment.Replies != null) + { + foreach (var reply in comment.Replies) + { + if (!studentsCache.ContainsKey(reply.UserId)) + { + var replyAuthor = await StudentsService.GetStudentAsync(reply.UserId); + if (replyAuthor != null) + { + studentsCache[reply.UserId] = replyAuthor; + } + } + } + } + } + + return comments; + } + + private CommentContext DetermineCommentContext() + { + if (Post.OrganizationId.HasValue) + { + return Post.EventId.HasValue ? CommentContext.OrganizationEvent : CommentContext.OrganizationPost; + } + else + { + return Post.EventId.HasValue ? CommentContext.UserEvent : CommentContext.UserPost; + } + } + + private void ToggleCommentSection() + { + IsCommentSectionVisible = !IsCommentSectionVisible; + NewCommentText = string.Empty; + } + + private void ToggleReplySection(Guid commentId) + { + if (activeReplyCommentId == commentId) + { + activeReplyCommentId = null; + newReplyText = string.Empty; + } + else + { + activeReplyCommentId = commentId; + newReplyText = string.Empty; + } + } + + private async Task SubmitCommentAsync() + { + if (string.IsNullOrWhiteSpace(NewCommentText)) + { + return; + } + + var commentContext = DetermineCommentContext(); + var userId = IdentityService.GetCurrentUserId(); + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: Post.Id, + commentContext: commentContext.ToString(), + userId: userId, + parentId: Guid.Empty, + textContent: NewCommentText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + NewCommentText = string.Empty; + Comments = await LoadCommentsForPostAsync(); + } + else + { + // Handle error (e.g., show a snackbar) + } + } + + private async Task SubmitReplyAsync(CommentDto parentComment) + { + if (string.IsNullOrWhiteSpace(newReplyText)) + { + return; + } + + var commentContext = DetermineCommentContext(); + var userId = IdentityService.GetCurrentUserId(); + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: Post.Id, + commentContext: commentContext.ToString(), + userId: userId, + parentId: parentComment.Id, + textContent: newReplyText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + newReplyText = string.Empty; + Comments = await LoadCommentsForPostAsync(); + activeReplyCommentId = null; + } + else + { + // Handle error (e.g., show a snackbar) + } + } + + private async Task HandleReactionAsync(ReactionType reactionType) + { + var reactions = await ReactionsService.GetReactionsAsync(Post.Id, ReactionContentType.Post); + var existingReaction = reactions.FirstOrDefault(r => r.UserId == IdentityService.UserDto.Id); + + if (existingReaction != null) + { + var updateReaction = new UpdateReactionDto + { + ReactionId = existingReaction.Id, + UserId = IdentityService.UserDto.Id, + NewReactionType = reactionType.ToString(), + ContentType = "Post", + TargetType = Post.OrganizationId.HasValue ? "Organization" : "User" + }; + + var updateResult = await ReactionsService.UpdateReactionAsync(updateReaction); + + if (updateResult.IsSuccessStatusCode) + { + // Reaction updated successfully + } + else + { + // Handle error (e.g., show a snackbar) + } + } + else + { + var createReaction = new CreateReactionDto + { + UserId = IdentityService.UserDto.Id, + ContentId = Post.Id, + ContentType = "Post", + ReactionType = reactionType.ToString(), + TargetType = Post.OrganizationId.HasValue ? "Organization" : "User" + }; + + var createResult = await ReactionsService.CreateReactionAsync(createReaction); + + if (createResult.IsSuccessStatusCode) + { + // Reaction added successfully + } + else + { + // Handle error (e.g., show a snackbar) + } + } + + ReactionsSummary = await ReactionsService.GetReactionsSummaryAsync(Post.Id, ReactionContentType.Post); // Refresh reactions summary + } + + private async Task AddLikeToCommentAsync(CommentDto comment) + { + var command = new AddLikeDto(comment.Id, IdentityService.GetCurrentUserId(), DetermineCommentContext().ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Comments = await LoadCommentsForPostAsync(); // Refresh comments + } + else + { + // Handle error (e.g., show a snackbar) + } + } + + private async Task AddLikeToReplyAsync(ReplyDto reply) + { + var command = new AddLikeDto(reply.Id, IdentityService.GetCurrentUserId(), DetermineCommentContext().ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Comments = await LoadCommentsForPostAsync(); // Refresh comments + } + else + { + // Handle error (e.g., show a snackbar) + } + } + + private string GetUserAvatar(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return studentsCache[userId.Value].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return $"{studentsCache[userId.Value].FirstName} {studentsCache[userId.Value].LastName}"; + } + return "Unknown User"; + } + + private void NavigateToPostDetails() + { + NavigationManager.NavigateTo($"/posts/details/{Post.Id}"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/UserInformation.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/UserInformation.razor new file mode 100644 index 000000000..714c1653e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/UserInformation.razor @@ -0,0 +1,445 @@ +@page "/user-information/{UserId:guid}" +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.Students +@using Astravent.Web.Wasm.Areas.Events +@inject IStudentsService StudentsService +@inject IEventsService EventsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using MudBlazor +@inject IIdentityService IdentityService +@inject AuthenticationStateProvider AuthenticationStateProvider + + + + + + + + @if (profileLoadFailed) + { + Failed to load user profile. Please try again later. + } + else if (student != null) + { +
+ +
+ @($"{student.FirstName} {student.LastName}") + @student.Description + Edit Profile + Public Profile +
+
+ } + else + { + + } +
+
+
+ + + @if (educationLoadFailed) + { + + Failed to load education details. + + } + else if (student?.Education != null && student.Education.Any()) + { + + + + + Education + + + @foreach (var education in student.Education) + { + @education.Degree at @education.InstitutionName + @education.StartDate?.ToString("MMMM yyyy") - @education.EndDate?.ToString("MMMM yyyy") + } + + + + } + else + { + + + + } + + + @if (workLoadFailed) + { + + Failed to load work experience. + + } + else if (student?.Work != null && student.Work.Any()) + { + + + + + Work Experience + + + @foreach (var work in student.Work) + { + @work.Position at @work.Company + @work.StartDate?.ToString("MMMM yyyy") - @work.EndDate?.ToString("MMMM yyyy") + } + + + + } + else + { + + + + } + + + @if (languagesLoadFailed) + { + + Failed to load languages and interests. + + } + else if ((student?.Languages != null && student.Languages.Any()) || (student?.Interests != null && student.Interests.Any())) + { + + + + + Skills and Interests + + + @if (student.Languages != null && student.Languages.Any()) + { + + Languages: + @foreach (var language in student.Languages) + { + @language + } + + } + @if (student.Interests != null && student.Interests.Any()) + { + + Interests: + @foreach (var interest in student.Interests) + { + @interest + } + + } + + + + } + else + { + + + + } + + + @if (eventsLoadFailed) + { + + Failed to load events. + + } + else if (loadingEvents) + { + + + + } + else if (InterestedInEventsDetails.Any() || SignedUpEventsDetails.Any()) + { + + + + + Events + + + @if (InterestedInEventsDetails.Any()) + { + Interested in: + @foreach (var eventDetail in InterestedInEventsDetails) + { + +
@eventDetail.Name - @eventDetail.StartDate.ToString("MMMM dd, yyyy")
+
+ } + } + @if (SignedUpEventsDetails.Any()) + { + Signed Up for: + @foreach (var eventDetail in SignedUpEventsDetails) + { + +
@eventDetail.Name - @eventDetail.StartDate.ToString("MMMM dd, yyyy")
+
+ } + } +
+
+
+ } +
+
+ +@code { + [Parameter] + public Guid UserId { get; set; } + + private StudentDto student; + private List InterestedInEventsDetails = new List(); + private List SignedUpEventsDetails = new List(); + + // Individual section loading and error state flags + private bool profileLoadFailed = false; + private bool educationLoadFailed = false; + private bool workLoadFailed = false; + private bool languagesLoadFailed = false; + private bool loadingEvents = true; + private bool eventsLoadFailed = false; + + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + Console.WriteLine($"User ID: {UserId}"); + var user = authState.User; + Console.WriteLine($"User authenticated: {user.Identity.IsAuthenticated}"); + + if (user.Identity.IsAuthenticated) + { + var userId = await IdentityService.GetCurrentUserIdFromJwtAsync(); + if (userId != null) + { + UserId = userId; + await LoadProfileAsync(); + await Task.WhenAll(LoadEventsAsync(), LoadEducationAsync(), LoadWorkExperienceAsync(), LoadLanguagesAndInterestsAsync()); + } + else + { + profileLoadFailed = true; + Snackbar.Add("Failed to obtain user ID from claims.", Severity.Error); + } + } + else + { + NavigationManager.NavigateTo("/signin"); + } + } + private async Task LoadProfileAsync() + { + try + { + student = await StudentsService.GetStudentAsync(UserId); + } + catch (Exception ex) + { + profileLoadFailed = true; + Snackbar.Add($"Error loading user profile: {ex.Message}", Severity.Error); + } + } + + private async Task LoadEducationAsync() + { + try + { + // Assume the education details are part of the student data + } + catch (Exception ex) + { + educationLoadFailed = true; + Snackbar.Add($"Error loading education details: {ex.Message}", Severity.Error); + } + } + + private async Task LoadWorkExperienceAsync() + { + try + { + // Assume work details are part of the student data + } + catch (Exception ex) + { + workLoadFailed = true; + Snackbar.Add($"Error loading work experience: {ex.Message}", Severity.Error); + } + } + + private async Task LoadLanguagesAndInterestsAsync() + { + try + { + // Assume languages and interests are part of the student data + } + catch (Exception ex) + { + languagesLoadFailed = true; + Snackbar.Add($"Error loading languages and interests: {ex.Message}", Severity.Error); + } + } + + private async Task LoadEventsAsync() + { + try + { + if (student?.InterestedInEvents != null) + { + foreach (var eventId in student.InterestedInEvents) + { + var eventDetail = await EventsService.GetEventAsync(eventId); + InterestedInEventsDetails.Add(eventDetail); + } + } + + if (student?.SignedUpEvents != null) + { + foreach (var eventId in student.SignedUpEvents) + { + var eventDetail = await EventsService.GetEventAsync(eventId); + SignedUpEventsDetails.Add(eventDetail); + } + } + } + catch (Exception ex) + { + eventsLoadFailed = true; + Snackbar.Add($"Error loading events: {ex.Message}", Severity.Error); + } + finally + { + loadingEvents = false; + } + } + + private string GetProfileImage() + { + var defaultImage = "images/default_profile_image.webp"; + return !string.IsNullOrEmpty(student?.ProfileImageUrl) ? student.ProfileImageUrl : defaultImage; + } + + private void NavigateToProfileSettings() + { + NavigationManager.NavigateTo("/account"); + } + + private void NavigateToPublicProfile() + { + NavigationManager.NavigateTo($"/user-details/{UserId}"); + } +} + + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/UserRelatedContent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/UserRelatedContent.razor new file mode 100644 index 000000000..753a57f87 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Feeds/UserRelatedContent.razor @@ -0,0 +1,69 @@ +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.Events +@inject IEventsService EventsService +@using MudBlazor + + + Event Recommendations + + @if (loadingRecommendations) + { + + } + else if (recommendations != null && recommendations.Any()) + { + + @foreach (var recommendation in recommendations) + { + + + + + + + @recommendation.Name + @recommendation.StartDate.ToString("MMMM dd, yyyy") + @recommendation.Description + + + + + } + + } + else + { + No recommendations available at the moment. + } + + +@code { + [Parameter] + public Guid UserId { get; set; } + + private List recommendations; + private bool loadingRecommendations = true; + + protected override async Task OnInitializedAsync() + { + recommendations = await FetchRecommendationsAsync(UserId); + loadingRecommendations = false; + } + + private async Task> FetchRecommendationsAsync(Guid userId) + { + try + { + var result = await EventsService.GetUserEventsFeedAsync(userId, pageNumber: 1, pageSize: 10, sortBy: "PublishDate", direction: "asc"); + return result.Items.ToList(); + } + catch (Exception ex) + { + return new List(); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/Friends.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/Friends.razor new file mode 100644 index 000000000..57b5cf6cc --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/Friends.razor @@ -0,0 +1,287 @@ +@page "/friends" +@using Astravent.Web.Wasm.HttpClients +@using Astravent.Web.Wasm.Areas.Friends +@using Astravent.Web.Wasm.DTO +@inject IIdentityService IdentityService +@inject IFriendsService FriendsService +@inject NavigationManager NavigationManager +@inject Radzen.NotificationService NotificationService +@inject Radzen.DialogService DialogService +@using MudBlazor + + + + + + +
+
+ + + @if (!pageInitialized) + { +
+ +
+ } + else if (friends != null && friends.Any()) + { + foreach (var friend in friends) + { +
+
+ Friend Image +
+
@friend.StudentDetails.FirstName @friend.StudentDetails.LastName
+

@friend.StudentDetails.Email

+
+
+ + + Details + + + + Remove + +
+
+
+ } + + @if (hasMorePages) + { + + + Load More + + } + } + else + { +

No friends to show. Start connecting now!

+ } +
+
+
+
+ + + +@code { + private List friends = new List(); + private string searchTerm; + private bool pageInitialized; + private int currentPage = 1; + private int pageSize = 10; + private int totalFriends; + private bool hasMorePages => friends.Count < totalFriends; + + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Search", href: "/friends/search", icon: Icons.Material.Filled.PersonSearch), + new BreadcrumbItem("Friends", href: "/friends", disabled: true, icon: Icons.Material.Filled.LibraryAddCheck), + }; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + await LoadFriends(); + pageInitialized = true; + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private string GetProfileImageUrl(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "images/default_profile_image.webp" : profileImageUrl; + } + + private void ViewDetails(Guid friendId) + { + NavigationManager.NavigateTo($"/user-details/{friendId}"); + } + + private async Task RemoveFriend(Guid friendId) + { + await FriendsService.RemoveFriendAsync(friendId); + friends = friends.Where(f => f.FriendId != friendId).ToList(); + NotificationService.Notify(Radzen.NotificationSeverity.Warning, "Friend Removed", "You have removed a friend.", 5000); + StateHasChanged(); + } + + private async Task ConfirmRemoveFriend(Guid friendId) + { + var confirm = await DialogService.Confirm("Are you sure you want to remove this friend?", "Confirm Removal", new Radzen.ConfirmOptions() { OkButtonText = "Yes", CancelButtonText = "No" }); + if (confirm.HasValue && confirm.Value) + { + await RemoveFriend(friendId); + } + } + + private void SearchFriends() + { + if (!string.IsNullOrWhiteSpace(searchTerm)) + { + searchTerm = searchTerm.Trim(); + friends = friends.Where(f => f.StudentDetails.FirstName.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) || f.StudentDetails.LastName.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)).ToList(); + } + else + { + ReloadFriends().Wait(); + } + StateHasChanged(); + } + + private async Task ReloadFriends() + { + currentPage = 1; + friends.Clear(); + await LoadFriends(); + } + + private async Task LoadFriends() + { + try + { + var studentId = IdentityService.GetCurrentUserId(); + var result = await FriendsService.GetAllFriendsAsync(studentId, currentPage, pageSize); + if (result != null) + { + friends.AddRange(result.Items); + totalFriends = result.TotalItems; + } + } + catch (Exception ex) + { + NotificationService.Notify(Radzen.NotificationSeverity.Error, "Failed to Load Friends", $"An error occurred: {ex.Message}", 5000); + } + } + + private async Task LoadMoreFriends() + { + currentPage++; + await LoadFriends(); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/FriendsRequests.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/FriendsRequests.razor new file mode 100644 index 000000000..c95eda9a6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/FriendsRequests.razor @@ -0,0 +1,297 @@ +@page "/friends/requests" +@using Astravent.Web.Wasm.HttpClients +@using MudBlazor +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.Areas.Friends +@inject IFriendsService FriendsService +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.Identity +@inject IIdentityService IdentityService +@inject Radzen.NotificationService NotificationService +@inject IJSRuntime JSRuntime + + + + + + +

Incoming Friend Requests

+ +
+
+ + @if (!pageInitialized) + { +
+ +
+ } + else if (filteredIncomingRequests != null && filteredIncomingRequests.Any()) + { + @foreach (var request in filteredIncomingRequests) + { +
+
+ Inviter Image +
+
@request.InviterName
+

@request.InviterEmail

+

@request.RequestedAt.ToLocalTime().ToString("yyyy-MM-dd")

+

@request.State

+
+
+ + + Decline + + + + Accept + +
+
+
+ } + + @if (hasMorePages) + { + + + Load More + + } + } + else + { +

No incoming requests.

+ } +
+
+
+
+ + + +@code { + private string searchTerm; + private List incomingRequests = new List(); + private List filteredIncomingRequests = new List(); + private bool pageInitialized = false; + private int currentPage = 1; + private int pageSize = 10; + private int totalRequests; + private bool hasMorePages => incomingRequests.Count < totalRequests; + + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Search", href: "/friends/search", icon: Icons.Material.Filled.PersonSearch), + new BreadcrumbItem("Friends", href: "/friends", icon: Icons.Material.Filled.LibraryAddCheck), + new BreadcrumbItem("Requests", href: "/friends/requests", disabled: true, icon: Icons.Material.Filled.GroupAdd), + }; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await LoadIncomingRequests(); + pageInitialized = true; + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private string GetProfileImageUrl(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "images/default_profile_image.webp" : profileImageUrl; + } + + private async Task LoadIncomingRequests() + { + try + { + var result = await FriendsService.GetIncomingFriendRequestsAsync(currentPage, pageSize); + if (result != null) + { + incomingRequests.AddRange(result.Items); + totalRequests = result.TotalItems; + filteredIncomingRequests = incomingRequests; + } + } + catch (Exception ex) + { + NotificationService.Notify(Radzen.NotificationSeverity.Error, "Failed to Load Requests", $"An error occurred: {ex.Message}", 5000); + } + } + + private async Task LoadMoreRequests() + { + currentPage++; + await LoadIncomingRequests(); + } + + private void SearchIncomingRequests() + { + filteredIncomingRequests = string.IsNullOrWhiteSpace(searchTerm) + ? incomingRequests + : incomingRequests.Where(x => x.InviterName.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)).ToList(); + } + + private void RedirectToDetails(Guid id) + { + NavigationManager.NavigateTo($"/user-details/{id}"); + } + + private async Task AcceptRequest(Guid requestId) + { + var request = incomingRequests.FirstOrDefault(r => r.Id == requestId); + if (request != null) + { + await FriendsService.AcceptFriendRequestAsync(new FriendRequestActionDto + { + RequestId = requestId, + RequesterId = request.InviterId, + FriendId = request.InviteeId + }); + await JSRuntime.InvokeVoidAsync("playNotificationSound"); + NotificationService.Notify(Radzen.NotificationSeverity.Success, "Friend Request Accepted", duration: 4000); + incomingRequests.Remove(request); + SearchIncomingRequests(); // Reapply search filter to update the UI + } + } + + private async Task DeclineRequest(Guid requestId) + { + var request = incomingRequests.FirstOrDefault(r => r.Id == requestId); + if (request != null) + { + await FriendsService.DeclineFriendRequestAsync(new FriendRequestActionDto + { + RequestId = requestId, + RequesterId = request.InviterId, + FriendId = request.InviteeId + }); + incomingRequests.Remove(request); + SearchIncomingRequests(); // Reapply search filter to update the UI + NotificationService.Notify(Radzen.NotificationSeverity.Warning, "Request Declined", duration: 4000); + } + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/FriendsSearch.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/FriendsSearch.razor new file mode 100644 index 000000000..73deecf1f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/FriendsSearch.razor @@ -0,0 +1,299 @@ +@page "/friends/search" +@using Astravent.Web.Wasm.HttpClients +@using MudBlazor +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.Areas.Friends +@inject IFriendsService FriendsService +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.Identity +@inject IIdentityService IdentityService +@inject IJSRuntime JSRuntime +@inject ISnackbar Snackbar + + + + + + + + + + + Search + + + + + + @if (!pageInitialized) + { + + } + else if (students.Any()) + { + + @foreach (var student in students) + { + + + + + + + + @($"{student.FirstName} {student.LastName}") + + @if (student.Id != IdentityService.GetCurrentUserId() && !(sentRequests?.Any(r => r.InviteeId == student.Id) ?? false) && !(allFriends?.Any(f => f.FriendId == student.Id) ?? false)) + { + + + Connect + + } + else if (allFriends?.Any(f => f.FriendId == student.Id) == true) + { + + + Connected + + } + else if (sentRequests?.Any(r => r.InviteeId == student.Id) == true) + { + + + Pending + + } + else if (student.Id == IdentityService.GetCurrentUserId()) + { + + + It's You + + } + + + + + + } + + } + else + { + No students found. Try searching with a different term. + } + + + + + +@code { + private string searchTerm; + private List students = new List(); + private IEnumerable sentRequests = Enumerable.Empty(); + private IEnumerable allFriends = Enumerable.Empty(); + private IEnumerable incomingRequests = Enumerable.Empty(); + private bool pageInitialized = false; + private int currentPage = 1; + private int pageSize = 10; + private int totalStudents; + + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Search", href: "/friends/search", disabled: true, icon: Icons.Material.Filled.PersonSearch) + }; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + try + { + await LoadStudents(); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to Load Data: {ex.Message}", Severity.Error); + } + finally + { + pageInitialized = true; + } + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private string GetProfileImageUrl(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "images/default_profile_image.webp" : profileImageUrl; + } + + private async Task LoadStudents(string searchArgument = null) + { + var response = await FriendsService.GetAllStudentsAsync(currentPage, pageSize, searchArgument); + + // Log the response object to the console + var jsonResponse = System.Text.Json.JsonSerializer.Serialize(response, new System.Text.Json.JsonSerializerOptions { WriteIndented = true }); + Console.WriteLine("Response JSON:"); + Console.WriteLine(jsonResponse); + + if (response?.Results != null) + { + students.AddRange(response.Results); + totalStudents = response.Total; + } + } + + private async Task SearchFriends() + { + currentPage = 1; + students.Clear(); + await LoadStudents(searchTerm); + } + + private async Task ConnectWithStudent(Guid studentId, MouseEventArgs e) + { + try + { + var currentUserId = IdentityService.GetCurrentUserId(); + await FriendsService.InviteStudent(currentUserId, studentId); + + var student = students.FirstOrDefault(s => s.Id == studentId); + if (student != null) + { + student.InvitationSent = true; + } + + sentRequests = (await FriendsService.GetSentFriendRequestsAsync(currentPage, pageSize))?.Items ?? Enumerable.Empty(); + + Snackbar.Add("The invitation has been successfully sent.", Severity.Success); + await JSRuntime.InvokeVoidAsync("playNotificationSound"); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to Send Invitation: {ex.Message}", Severity.Error); + } + finally + { + StateHasChanged(); + } + } + + private void ViewDetails(Guid studentId) + { + NavigationManager.NavigateTo($"/user-details/{studentId}"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/SentRequests.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/SentRequests.razor new file mode 100644 index 000000000..de071e339 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/SentRequests.razor @@ -0,0 +1,280 @@ +@page "/friends/sent-requests" +@using Astravent.Web.Wasm.HttpClients +@using MudBlazor +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.Areas.Friends +@inject IFriendsService FriendsService +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Areas.Identity +@inject IIdentityService IdentityService +@inject Radzen.NotificationService NotificationService +@inject IJSRuntime JSRuntime + + + + + +

Sent Friend Requests

+ +
+
+ + @if (!pageInitialized) + { +
+ +
+ } + else if (filteredSentRequests != null && filteredSentRequests.Any()) + { + @foreach (var request in filteredSentRequests) + { +
+
+ Invitee Image +
+
@request.InviteeName
+

@request.InviteeEmail

+

@request.RequestedAt.ToLocalTime().ToString("yyyy-MM-dd")

+

@request.State

+
+
+ + + Withdraw + +
+
+
+ } + + @if (hasMorePages) + { + + + Load More + + } + } + else + { +

No sent requests.

+ } +
+
+
+
+ + + +@code { + private string searchTerm; + private List sentRequests = new List(); + private List filteredSentRequests = new List(); + private bool pageInitialized = false; + private int currentPage = 1; + private int pageSize = 10; + private int totalRequests; + private bool hasMorePages => sentRequests.Count < totalRequests; + + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Search", href: "/friends/search", icon: Icons.Material.Filled.PersonSearch), + new BreadcrumbItem("Friends", href: "/friends", icon: Icons.Material.Filled.LibraryAddCheck), + new BreadcrumbItem("Requests", href: "/friends/requests", icon: Icons.Material.Filled.GroupAdd), + new BreadcrumbItem("Sent Requests", href: "/friends/sent-requests", disabled: true, icon: Icons.Material.Filled.PersonAddAlt1), + }; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await LoadSentRequests(); + pageInitialized = true; + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private string GetProfileImageUrl(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "images/default_profile_image.webp" : profileImageUrl; + } + + private async Task LoadSentRequests() + { + try + { + var result = await FriendsService.GetSentFriendRequestsAsync(currentPage, pageSize); + if (result != null) + { + sentRequests.AddRange(result.Items); + totalRequests = result.TotalItems; + filteredSentRequests = sentRequests; + } + } + catch (Exception ex) + { + NotificationService.Notify(Radzen.NotificationSeverity.Error, "Failed to Load Requests", $"An error occurred: {ex.Message}", 5000); + } + } + + private async Task LoadMoreSentRequests() + { + currentPage++; + await LoadSentRequests(); + } + + private void SearchSentRequests() + { + filteredSentRequests = string.IsNullOrWhiteSpace(searchTerm) + ? sentRequests + : sentRequests.Where(x => x.InviteeName.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)).ToList(); + } + + private void RedirectToDetails(Guid id) + { + NavigationManager.NavigateTo($"/user-details/{id}"); + } + + private async Task WithdrawRequest(Guid inviteeId) + { + var inviterId = IdentityService.GetCurrentUserId(); + if (inviterId != Guid.Empty) + { + await FriendsService.WithdrawFriendRequestAsync(new WithdrawFriendRequestDto + { + InviterId = inviterId, + InviteeId = inviteeId + }); + + sentRequests = new List((await FriendsService.GetSentFriendRequestsAsync(currentPage, pageSize)).Items); + SearchSentRequests(); // Reapply search filter to update the UI + NotificationService.Notify(Radzen.NotificationSeverity.Warning, "Request Withdrawn", "You have successfully withdrawn the friend request.", 5000); + } + else + { + NotificationService.Notify(Radzen.NotificationSeverity.Error, "Error", "Invalid user ID."); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/UserDetails.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/UserDetails.razor new file mode 100644 index 000000000..6b48cae76 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Friends/UserDetails.razor @@ -0,0 +1,681 @@ +@page "/user-details/{Id:guid}" +@using Astravent.Web.Wasm.Areas.Friends +@using Astravent.Web.Wasm.Areas.Organizations +@using Astravent.Web.Wasm.Areas.Students +@inject NavigationManager NavigationManager +@inject IFriendsService FriendsService +@inject IEventsService EventsService +@inject IOrganizationsService OrganizationsService +@inject ISnackbar Snackbar +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@using Astravent.Web.Wasm.DTO +@using MudBlazor + + + + @if (studentNotFound) + { + + Student profile not found! + It may have been deleted or is inaccessible. + + } + else if (student == null) + { + + } + else + { + + + + + + + @student.FirstName @student.LastName + + @if (!string.IsNullOrWhiteSpace(student.Description)) + { + @student.Description + } + + + + @if (student.DateOfBirth.HasValue) + { + Date of Birth: @student.DateOfBirth?.ToLocalTime().ToString("yyyy-MM-dd") + } + + @if (!string.IsNullOrWhiteSpace(student.City) || !string.IsNullOrWhiteSpace(student.Country)) + { + Location: @student.City, @student.Country + } + + @if (!string.IsNullOrWhiteSpace(student.State)) + { + State: @student.State + } + + + Joined: @student.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd") + + + + + + + @if (ShouldDisplayGallery(student.UserSettings.GalleryVisibility) && student.GalleryOfImageUrls?.Any(img => IsValidImageUrl(img.ImageUrl)) == true) + { + + + Gallery + + + + @foreach (var galleryImage in student.GalleryOfImageUrls.Where(img => IsValidImageUrl(img.ImageUrl))) + { + + + + + + } + + + + } + + + + @if (followers?.Any() == true) + { + + + Followers + + + + @foreach (var follower in followers) + { + + + + + @follower.StudentDetails.FirstName @follower.StudentDetails.LastName + + } + + + + } + + + + @if (following?.Any() == true) + { + + + Following + + + + @foreach (var follow in following) + { + + + + + @follow.StudentDetails.FirstName @follow.StudentDetails.LastName + + } + + + + } + + + + @if (friends?.Any() == true) + { + + + Friends + + + + @foreach (var friend in friends) + { + + + + + @friend.StudentDetails.FirstName @friend.StudentDetails.LastName + + } + + + + } + + + + + + @if (sentFriendRequests?.Any() == true) + { + + + Sent Friend Requests + + + + @foreach (var request in sentFriendRequests) + { + + @request.InviteeName + + } + + + + } + + + @if (incomingFriendRequests?.Any() == true) + { + + + Incoming Friend Requests + + + + @foreach (var request in incomingFriendRequests) + { + + @request.InviterName + + } + + + + } + + + + + + @if (InterestedInEventsDetails?.Any() == true) + { + + + Interested Events + + + + @foreach (var eventDetail in InterestedInEventsDetails) + { + + + + + + + @eventDetail.Name - @eventDetail.StartDate.ToString("MMMM dd, yyyy") + + + + } + + + + } + + + @if (userEvents?.Any() == true) + { + + + Signed Up Events + + + + @foreach (var eventDto in userEvents) + { + + + + + + + @eventDto.Name - @eventDto.StartDate.ToString("MMMM dd, yyyy") + + + + } + + + + } + + + + + + @if (userOrganizations?.Any() == true) + { + + + User's Organizations + + + + @foreach (var organization in userOrganizations) + { + + + + + + + + @organization.Name + @organization.Description + @organization.UserCount users + + + + } + + + + } + + + + @if (followedOrganizations?.Any() == true) + { + + + Organizations Followed + + + + @foreach (var organization in followedOrganizations) + { + + + + + + + + @organization.OrganizationDetails.Name + @organization.OrganizationDetails.Description + @organization.OrganizationDetails.Users.Count() users + + + + } + + + + } + + + } + + + + + +@code { + [Parameter] public Guid Id { get; set; } + private StudentDto student; + private Guid currentUserId; + private bool studentNotFound; + private List userEvents = new List(); + private List InterestedInEventsDetails = new List(); + private List userOrganizations = new List(); + private List followedOrganizations = new List(); + private List friends; + private List followers; + private List following; + private List sentFriendRequests; + private List incomingFriendRequests; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + currentUserId = IdentityService.GetCurrentUserId(); + student = await FriendsService.GetStudentAsync(Id); + if (student == null) + { + studentNotFound = true; + } + else + { + await LoadUserData(); + } + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private async Task LoadUserData() + { + var pagedEvents = await EventsService.GetUserEventsAsync(Id, 1, 10, "signed_up"); + userEvents = pagedEvents?.Items?.ToList() ?? new List(); + + friends = (await FriendsService.GetAllFriendsAsync(Id, 1, 10))?.Items?.ToList() ?? new List(); + followers = (await FriendsService.GetPagedFollowersAsync(Id, 1, 10))?.Items?.ToList() ?? new List(); + following = (await FriendsService.GetPagedFollowingAsync(Id, 1, 10))?.Items?.ToList() ?? new List(); + + var sentRequestsPaged = await FriendsService.GetSentFriendRequestsAsync(1, 10); + sentFriendRequests = sentRequestsPaged?.Items?.ToList() ?? new List(); + + var incomingRequestsPaged = await FriendsService.GetIncomingFriendRequestsAsync(1, 10); + incomingFriendRequests = incomingRequestsPaged?.Items?.ToList() ?? new List(); + + await LoadInterestedAndSignedUpEventsAsync(); + await LoadUserOrganizationsAsync(); + await LoadFollowedOrganizationsAsync(); + } + + private async Task LoadInterestedAndSignedUpEventsAsync() + { + try + { + // Load interested events + if (student.InterestedInEvents != null && student.InterestedInEvents.Any()) + { + foreach (var eventId in student.InterestedInEvents) + { + var eventDetail = await EventsService.GetEventAsync(eventId); + if (eventDetail != null) + { + InterestedInEventsDetails.Add(eventDetail); + } + } + } + + // Load signed-up events + if (student.SignedUpEvents != null && student.SignedUpEvents.Any()) + { + foreach (var eventId in student.SignedUpEvents) + { + var eventDetail = await EventsService.GetEventAsync(eventId); + if (eventDetail != null) + { + userEvents.Add(eventDetail); + } + } + } + } + catch (Exception ex) + { + Snackbar.Add($"Error loading events: {ex.Message}", Severity.Error); + } + } + + private async Task LoadUserOrganizationsAsync() + { + try + { + var pagedOrganizations = await OrganizationsService.GetPaginatedUserOrganizationsAsync(Id, 1, 10); + userOrganizations = pagedOrganizations?.Items?.ToList() ?? new List(); + } + catch (Exception ex) + { + Snackbar.Add($"Error loading user's organizations: {ex.Message}", Severity.Error); + } + } + + private async Task LoadFollowedOrganizationsAsync() + { + try + { + var organizations = await OrganizationsService.GetUserFollowedOrganizationsAsync(Id); + followedOrganizations = organizations?.ToList() ?? new List(); + } + catch (Exception ex) + { + Snackbar.Add($"Error loading followed organizations: {ex.Message}", Severity.Error); + } + } + + private string GetProfileImageUrl(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "/images/default_profile_image.webp" : profileImageUrl; + } + + private string GetBannerImageUrl(string bannerImageUrl) + { + return string.IsNullOrEmpty(bannerImageUrl) ? "/images/default_banner_image.png" : bannerImageUrl; + } + + private bool IsValidImageUrl(string url) + { + if (string.IsNullOrEmpty(url)) + return false; + + string[] validExtensions = { ".jpg", ".jpeg", ".png", ".gif", ".webp" }; + string extension = System.IO.Path.GetExtension(url)?.ToLower(); + return validExtensions.Contains(extension); + } + + private bool IsFriend(FriendDto friend) + { + return friends.Any(f => f.FriendId == friend.FriendId); + } + + private bool ShouldDisplayGallery(DTO.Visibility galleryVisibility) + { + return galleryVisibility == Visibility.Everyone || (galleryVisibility == Visibility.Connections && IsFriend(null)); + } + + private async Task AddFriend() + { + if (student != null) + { + await FriendsService.AddFriendAsync(student.Id); + Snackbar.Add("Friend request sent.", Severity.Success); + } + } + + private async Task BlockUser() + { + if (student != null) + { + await StudentsService.BlockUserAsync(currentUserId, student.Id); + Snackbar.Add("User blocked.", Severity.Warning); + } + } + + private void ReportUser() + { + // Implement the logic to report the user here + Snackbar.Add("User reported.", Severity.Info); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Greeting.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Greeting.razor new file mode 100644 index 000000000..305d6fd31 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Greeting.razor @@ -0,0 +1,23 @@ +@page "/greeting" +@using Microsoft.AspNetCore.Components.Authorization +@inject IIdentityService IdentityService +@using Blazored.LocalStorage +@inject ILocalStorageService localStorage + +

Greeting

+ + +@if (email != null) +{ +

Hello, @email!

+} +else +{ +

Loading...

+} + +@code { + private string email; + + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Index.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Index.razor new file mode 100644 index 000000000..02c3c036e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Index.razor @@ -0,0 +1,130 @@ +@page "/" +@using MudBlazor +@inject NavigationManager NavigationManager + + + + + + + @foreach (var img in images) + { + +
+
+ } +
+ + @foreach (var img in images2) + { + + +
+
+
+ } +
+
+
+

Welcome to Mini Space

+

@titles[activeIndex]

+

@descriptions[activeIndex]

+ NavigationManager.NavigateTo("events/search"))>Get Started +
+ +
+
+ + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ + + +@code { + private int activeIndex = 0; + private bool arrows = true; + private bool bullets = true; + private bool enableSwipeGesture = true; + private bool autocycle = true; + private MudBlazor.Transition transition = MudBlazor.Transition.Slide; + private List images = new List { "images/mini_1.webp", "images/pw_1.webp", "images/pw_2.webp" }; + private List images2 = new List { "images/students_1.webp", "images/students_2.webp", "images/students_3.webp" }; + private List titles = new List { "Exploration", "Connection", "Sharing" }; + private List descriptions = new List { + "Explore new places and meet new people on MiniSpace.", + "Connect with friends and family, share your experiences.", + "Share your adventures and stories with a global audience." + }; + + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/MediaFiles/Dialogs/ImageDialog.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/MediaFiles/Dialogs/ImageDialog.razor new file mode 100644 index 000000000..cb75c9603 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/MediaFiles/Dialogs/ImageDialog.razor @@ -0,0 +1,26 @@ +@using Radzen +@inject Radzen.DialogService DialogService + +@if (!string.IsNullOrEmpty(Base64Image)) +{ + + + +} + +@code { + [Parameter] + public string Base64Image { get; set; } + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + StateHasChanged(); + } + + private string GetImage() + { + return $"data:image/webp;base64,{Base64Image}"; + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/AllNotifications.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/AllNotifications.razor new file mode 100644 index 000000000..859166e03 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/AllNotifications.razor @@ -0,0 +1,163 @@ +@page "/notifications/all" +@using Astravent.Web.Wasm.Areas.Notifications +@inject INotificationsService NotificationsService +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Notifications +@inject IIdentityService IdentityService +@using MudBlazor +@using System.Collections.Generic +@using System.Threading.Tasks + + + + + + @code { + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Notifications", href: "/notifications/all", icon: Icons.Material.Filled.Notifications) + }; + } + +
+

All Notifications

+
+ + @if (notifications == null) + { +
+ +
+ } + else if (notifications.Any()) + { + + @foreach (var notification in notifications) + { + + + + + @notification.Message + @notification.CreatedAt.ToString("MMMM dd, yyyy") + + + + + + @if (notification.Status == "Unread") + { + + + Mark as Read + + } + else + { + + + Mark as Unread + + } + + + + + Delete + + + + + } + + } + else + { +

No notifications found.

+ } +
+
+ + + +@code { + private List notifications = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalNotifications; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + await LoadNotifications(); + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private async Task LoadNotifications() + { + var userId = IdentityService.GetCurrentUserId(); + var response = await NotificationsService.GetNotificationsByUserAsync(userId, page: currentPage, pageSize: pageSize, sortOrder: "desc", status: null); + + if (response != null) + { + notifications = response.Results; + totalNotifications = response.Total; + StateHasChanged(); + } + } + + private async Task UpdateNotificationStatus(Guid userId, Guid notificationId, string newStatus) + { + await NotificationsService.UpdateNotificationStatusAsync(userId, notificationId, newStatus); + var notification = notifications.Find(n => n.NotificationId == notificationId); + if (notification != null) + { + notification.Status = newStatus; + StateHasChanged(); + } + } + + private async Task DeleteNotification(Guid userId, Guid notificationId) + { + await NotificationsService.DeleteNotificationAsync(userId, notificationId); + notifications.RemoveAll(n => n.NotificationId == notificationId); + StateHasChanged(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/HistoryNotifications.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/HistoryNotifications.razor new file mode 100644 index 000000000..3239da072 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/HistoryNotifications.razor @@ -0,0 +1,161 @@ +@page "/notifications/history" +@using Astravent.Web.Wasm.Areas.Notifications +@inject INotificationsService NotificationsService +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Notifications +@using MudBlazor +@inject IIdentityService IdentityService +@using System.Collections.Generic +@using System.Threading.Tasks + + + + + @code { + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Notifications", href: "/notifications/all", icon: Icons.Material.Filled.Notifications), + new BreadcrumbItem("New Notifications", href: "/notifications/new", icon: Icons.Material.Filled.NotificationsActive), + new BreadcrumbItem("Notifications History", href: "/notifications/history", disabled: true, icon: Icons.Material.Filled.NotificationsPaused) + }; + } + +
+

Notifications History

+
+ + @if (notifications == null) + { +
+ +
+ } + else if (notifications.Any()) + { + + @foreach (var notification in notifications) + { + + + + + @notification.Message + @notification.CreatedAt.ToString("MMMM dd, yyyy") + + + + + @if (notification.Status == "Unread") + { + + + Mark as Read + + } + else + { + + + Mark as Unread + + } + + + + Delete + + + + + } + + } + else + { +

No notifications found.

+ } +
+ + + +@code { + private List notifications = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalNotifications; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + await LoadNotifications(); + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private async Task LoadNotifications() + { + var userId = IdentityService.GetCurrentUserId(); + var response = await NotificationsService.GetNotificationsByUserAsync(userId, page: currentPage, pageSize: pageSize, sortOrder: "desc", status: "Read"); + + if (response != null) + { + notifications = response.Results; + totalNotifications = response.Total; + StateHasChanged(); + } + } + + private async Task UpdateNotificationStatus(Guid userId, Guid notificationId, string newStatus) + { + await NotificationsService.UpdateNotificationStatusAsync(userId, notificationId, newStatus); + var notification = notifications.Find(n => n.NotificationId == notificationId); + if (notification != null) + { + notification.Status = newStatus; + StateHasChanged(); + } + } + + private async Task DeleteNotification(Guid userId, Guid notificationId) + { + await NotificationsService.DeleteNotificationAsync(userId, notificationId); + notifications.RemoveAll(n => n.NotificationId == notificationId); + StateHasChanged(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/NewNotifications.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/NewNotifications.razor new file mode 100644 index 000000000..918b2c6b2 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/NewNotifications.razor @@ -0,0 +1,160 @@ +@page "/notifications/new" +@using Astravent.Web.Wasm.Areas.Notifications +@inject INotificationsService NotificationsService +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Notifications +@inject IIdentityService IdentityService +@using MudBlazor +@using System.Collections.Generic +@using System.Threading.Tasks + + + + + @code { + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Notifications", href: "/notifications/all", icon: Icons.Material.Filled.Notifications), + new BreadcrumbItem("New Notifications", href: "/notifications/new", disabled: true, icon: Icons.Material.Filled.NotificationsActive) + }; + } + +
+

Recent Notifications

+
+ + @if (notifications == null) + { +
+ +
+ } + else if (notifications.Any()) + { + + @foreach (var notification in notifications) + { + + + + + @notification.Message + @notification.CreatedAt.ToString("MMMM dd, yyyy") + + + + + @if (notification.Status == "Unread") + { + + + Mark as Read + + } + else + { + + + Mark as Unread + + } + + + + Delete + + + + + } + + } + else + { +

No notifications found.

+ } +
+ + + +@code { + private List notifications = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalNotifications; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + await LoadNotifications(); + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private async Task LoadNotifications() + { + var userId = IdentityService.GetCurrentUserId(); + var response = await NotificationsService.GetNotificationsByUserAsync(userId, page: currentPage, pageSize: pageSize, sortOrder: "desc", status: "Unread"); + + if (response != null) + { + notifications = response.Results; + totalNotifications = response.Total; + StateHasChanged(); + } + } + + private async Task UpdateNotificationStatus(Guid userId, Guid notificationId, string newStatus) + { + await NotificationsService.UpdateNotificationStatusAsync(userId, notificationId, newStatus); + var notification = notifications.Find(n => n.NotificationId == notificationId); + if (notification != null) + { + notification.Status = newStatus; + StateHasChanged(); + } + } + + private async Task DeleteNotification(Guid userId, Guid notificationId) + { + await NotificationsService.DeleteNotificationAsync(userId, notificationId); + notifications.RemoveAll(n => n.NotificationId == notificationId); + StateHasChanged(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/Notification.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/Notification.razor new file mode 100644 index 000000000..55ae6e186 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Notifications/Notification.razor @@ -0,0 +1,163 @@ +@page "/notification/{NotificationId:guid}" +@using Astravent.Web.Wasm.Areas.Notifications +@inject NavigationManager NavigationManager +@inject INotificationsService NotificationsService +@using Astravent.Web.Wasm.DTO.Notifications +@inject IIdentityService IdentityService +@using System.Globalization +@using MudBlazor + + + +@code { + private List _items = new List + { + new BreadcrumbItem("Home", href: "/home", icon: Icons.Material.Filled.Home), + new BreadcrumbItem("Notifications", href: "/notifications/all", icon: Icons.Material.Filled.Notifications), + new BreadcrumbItem("New Notifications", href: "/notifications/new", icon: Icons.Material.Filled.NotificationsActive), + new BreadcrumbItem("Notifications History", href: "/notifications/history", icon: Icons.Material.Filled.NotificationsPaused), + new BreadcrumbItem("Notification", href: "/notification/{NotificationId:guid}", disabled: true, icon: @Icons.Material.Filled.NotificationAdd) + }; +} + + + + + + + + + +
+

Notification Details

+
+
+ @if (notification != null) + { + + + Message: @RenderMessage(notification) + + + Details: @RenderHtml(notification.Details) + + + Date: @notification.CreatedAt.ToLocalTime().ToString("f", CultureInfo.CurrentUICulture) + + + Status: @notification.Status + + + } + else + { + Loading notification details... + } +
+
+
+
+
+
+ + + +@code { + [Parameter] public Guid NotificationId { get; set; } + private NotificationDto notification; + + protected override async Task OnInitializedAsync() + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + var userId = IdentityService.GetCurrentUserId(); + notification = await NotificationsService.GetNotificationByIdAsync(userId, NotificationId); + if (notification.Status == "Unread") + { + await UpdateNotificationStatus(userId, NotificationId, "Read"); + } + } + else + { + NavigationManager.NavigateTo("/login"); + } + } + + private async Task UpdateNotificationStatus(Guid userId, Guid notificationId, string newStatus) + { + await NotificationsService.UpdateNotificationStatusAsync(userId, notificationId, newStatus); + notification.Status = newStatus; + StateHasChanged(); + } + + private MarkupString RenderMessage(NotificationDto notification) + { + string link = NotificationLinkFactory.GetNotificationLink(notification); + if (!string.IsNullOrEmpty(link)) + { + return new MarkupString($"{notification.Message} View Details"); + } + else + { + return new MarkupString(notification.Message); + } + } + + private MarkupString RenderHtml(string htmlContent) + { + return new MarkupString(htmlContent); + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/CreateEvent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/CreateEvent.razor new file mode 100644 index 000000000..06ab7b47e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/CreateEvent.razor @@ -0,0 +1,522 @@ +@page "/organizations/{OrganizationId:guid}/events/create" +@using Astravent.Web.Wasm.Areas.Events +@using Astravent.Web.Wasm.Areas.Events.CommandsDto +@using Astravent.Web.Wasm.DTO.Enums +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.Utilities +@inject IEventsService EventsService +@inject ISnackbar Snackbar +@inject IMediaFilesService MediaFilesService +@inject IIdentityService IdentityService +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager +@using MudBlazor +@using System.IO +@using System.Text.Json + + + + Organize Event for Organization + + @if (IsUploading) + { +
+ + Uploading Files, please wait... +
+ } + else + { + + + + + + General Information + + + + + Event Description + + + + + + + + + + + + + + + @foreach (var category in Enum.GetNames(typeof(Category))) + { + @category + } + + + + + + + + + + + + @if (IsLocationDefined) + { + + Location + + + + + + + + } + + + + + Payment + Requires Payment + + @if (newEvent.Settings.RequiresPayment) + { + + + @foreach (var method in Enum.GetNames(typeof(PaymentMethod))) + { + @method + } + + + + + + } + + + + + Media and Visibility + + + + + + + + Additional Setting + Requires Approval + Is Online Event + Is Private + Requires RSVP + Allows Guests + Show Attendees Publicly + Send Reminders + + Enable Chat + Allow Comments + Issue Tickets + Record Event + + + + + + + Submit + Create Event + + + + } +
+ + + + + +
+ +@code { + [Parameter] public Guid OrganizationId { get; set; } + + private CreateEventCommand newEvent = new CreateEventCommand(); + private MudForm form; + + private string bannerPreviewUrl; + private List mediaFilesPreviews = new(); + private string CroppedImageBase64; + + private IBrowserFile croppedImageFile; + private IReadOnlyList mediaFiles; + private bool IsUploading { get; set; } = false; + private string currentImageType = string.Empty; + + private string defaultBannerImage = "/images/default_banner_image.png"; + private string defaultMediaFileImage = "/images/default_media_file_image.png"; + + private DateTime? startDateTemp; + private TimeSpan? startTimeTemp; + private DateTime? endDateTemp; + private TimeSpan? endTimeTemp; + private DateTime? publishDateTemp; + private TimeSpan? publishTimeTemp; + private bool IsLoading { get; set; } = true; + private bool IsLocationDefined { get; set; } = false; + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + newEvent.OrganizerId = IdentityService.GetCurrentUserId(); + newEvent.OrganizerType = OrganizerType.Organization.ToString(); + newEvent.OrganizationId = OrganizationId; + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add($"Failed to initialize authentication state: {ex.Message}", Severity.Error); + } + finally + { + IsLoading = false; + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", DotNetObjectReference.Create(this)); + } + } + + private async Task UploadBannerClick() + { + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputBanner').click()"); + } + + private async Task UploadMediaFilesClick() + { + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputMediaFiles').click()"); + } + + private async Task OpenCropper(InputFileChangeEventArgs e, string imageType) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageType = imageType; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, imageType); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); + + try + { + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + string imageType = "EventBanner"; + var response = await MediaFilesService.UploadMediaFileAsync( + sourceId: newEvent.EventId, + sourceType: imageType, + uploaderId: IdentityService.GetCurrentUserId(), + fileName: $"{newEvent.Name}_{currentImageType}.png", + fileContentType: croppedImageFile.ContentType, + fileData: fileData + ); + + var responseDto = response?.Content; + + if (response.IsSuccessStatusCode && responseDto != null) + { + var uploadedUrl = responseDto.ProcessedUrl; + if (!string.IsNullOrWhiteSpace(uploadedUrl)) + { + bannerPreviewUrl = uploadedUrl; + newEvent.BannerUrl = bannerPreviewUrl; + StateHasChanged(); + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Uploaded URL is null or empty."); + } + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Failed to upload the image."); + } + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + finally + { + IsUploading = false; + StateHasChanged(); + } + + CloseCropper(); + } + } + + private async Task UploadMediaFiles(InputFileChangeEventArgs e) + { + const long maxAllowedSize = 10 * 1024 * 1024; + mediaFiles = e.GetMultipleFiles(); + + foreach (var file in mediaFiles) + { + if (file.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + } + + foreach (var file in mediaFiles) + { + using var stream = file.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + + var response = await MediaFilesService.UploadFileAsync( + sourceId: newEvent.EventId, + sourceType: "EventFile", + uploaderId: IdentityService.GetCurrentUserId(), + fileName: file.Name, + fileContentType: file.ContentType, + fileData: buffer + ); + + var responseDto = response.Content; + + if (response.IsSuccessStatusCode && responseDto != null) + { + var uploadedUrl = responseDto.FileUrl; + if (!string.IsNullOrWhiteSpace(uploadedUrl)) + { + mediaFilesPreviews.Add(new FilePreview + { + Url = uploadedUrl, + ContentType = file.ContentType, + IsImage = file.ContentType.StartsWith("image/"), + IsVideo = file.ContentType.StartsWith("video/"), + IsPdf = file.ContentType.EndsWith("pdf") + }); + ((List)newEvent.MediaFilesUrl).Add(uploadedUrl); + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Uploaded URL is null or empty."); + } + } + else + { + await JSRuntime.InvokeVoidAsync("alert", $"Failed to upload {file.Name}."); + } + } + + StateHasChanged(); + } + + private async Task SubmitEvent() + { + if (startDateTemp.HasValue && startTimeTemp.HasValue) + { + newEvent.StartDate = (startDateTemp.Value.Date + startTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (endDateTemp.HasValue && endTimeTemp.HasValue) + { + newEvent.EndDate = (endDateTemp.Value.Date + endTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (publishDateTemp.HasValue && publishTimeTemp.HasValue) + { + newEvent.PublishDate = (publishDateTemp.Value.Date + publishTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (!IsLocationDefined) + { + newEvent.BuildingName = null; + newEvent.Street = null; + newEvent.BuildingNumber = null; + newEvent.ApartmentNumber = null; + newEvent.City = null; + newEvent.ZipCode = null; + newEvent.Country = null; + } + + newEvent.OrganizationId = OrganizationId; + + await form.Validate(); + if (form.IsValid) + { + var formDataJson = JsonSerializer.Serialize(newEvent, new JsonSerializerOptions { WriteIndented = true }); + Console.WriteLine("Form data to be sent:"); + Console.WriteLine(formDataJson); + + var response = await EventsService.CreateEventAsync(newEvent); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Event created successfully!", Severity.Success); + NavigationManager.NavigateTo($"/organizations/{OrganizationId}/events"); + } + else + { + var errorMessage = "Failed to create event. Please try again."; + if (response.ErrorMessage != null) + { + errorMessage = $"Error: {response.ErrorMessage.Reason}"; + } + + Snackbar.Add(errorMessage, Severity.Error); + } + } + } + + private string GetBannerUrl() + { + return !string.IsNullOrWhiteSpace(bannerPreviewUrl) ? bannerPreviewUrl : defaultBannerImage; + } + + private class FilePreview + { + public string Url { get; set; } + public string ContentType { get; set; } + public bool IsImage { get; set; } + public bool IsVideo { get; set; } + public bool IsPdf { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/CreateOrganization.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/CreateOrganization.razor new file mode 100644 index 000000000..2ec62997c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/CreateOrganization.razor @@ -0,0 +1,148 @@ +@page "/organizations/create" +@inject IOrganizationsService OrganizationsService +@inject ISnackbar Snackbar +@inject IIdentityService IdentityService +@inject IMediaFilesService MediaFilesService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime +@using Astravent.Web.Wasm.Utilities +@using System.IO +@using Astravent.Web.Wasm.Areas.Organizations.CommandsDto +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Organizations +@using System.Collections.Generic +@using System.Linq +@using System.Threading.Tasks + + + Create New Organization + + + + + + +
+ + +
+ + @if (organizationModel.Settings != null) + { + Private Organization + Visible to Public + } + + + Create Organization + + + + Back + +
+
+
+ +@code { + private MudForm form; + private CreateOrganizationDto organizationModel = new CreateOrganizationDto(); + private bool _isSubmitting = false; + private List _parentOrganizations = new List(); + private string selectedParentId; + + protected override async Task OnInitializedAsync() + { + try + { + var userId = IdentityService.GetCurrentUserId(); + await LoadUserOrganizationsAsync(userId); + + selectedParentId = organizationModel.ParentId?.ToString(); + if (organizationModel.Settings == null) + { + organizationModel.Settings = new OrganizationSettingsDto(); + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load organizations: {ex.Message}", Severity.Error); + } + } + + private async Task LoadUserOrganizationsAsync(Guid userId) + { + var page = 1; + var pageSize = 100; + var allOrganizations = new List(); + + PagedResult pagedOrganizations; + do + { + pagedOrganizations = await OrganizationsService.GetPaginatedUserOrganizationsAsync(userId, page, pageSize); + allOrganizations.AddRange(pagedOrganizations.Items); + page++; + } while (pagedOrganizations.NextPage.HasValue); + + _parentOrganizations = allOrganizations.ToList(); + } + + private async Task SubmitForm() + { + await form.Validate(); + + if (form.IsValid) + { + _isSubmitting = true; + + try + { + organizationModel.OrganizationId = Guid.NewGuid(); + organizationModel.OwnerId = IdentityService.GetCurrentUserId(); + organizationModel.ParentId = string.IsNullOrEmpty(selectedParentId) ? null : new Guid?(new Guid(selectedParentId)); + + if (organizationModel.ParentId.HasValue) + { + var parentOrg = _parentOrganizations.FirstOrDefault(o => o.Id == organizationModel.ParentId); + organizationModel.RootId = parentOrg?.RootId ?? parentOrg?.Id; + } + else + { + organizationModel.RootId = organizationModel.OrganizationId; + } + + var response = await OrganizationsService.CreateOrganizationAsync(organizationModel); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Organization created successfully.", Severity.Success); + NavigationManager.NavigateTo($"/organizations/details/{organizationModel.OrganizationId}"); + } + else + { + Snackbar.Add("Failed to create organization.", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"An error occurred: {ex.Message}", Severity.Error); + } + + _isSubmitting = false; + } + } + + private void GoBack() + { + NavigationManager.NavigateTo("/organizations/my"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EditEventComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EditEventComponent.razor new file mode 100644 index 000000000..5836f8598 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EditEventComponent.razor @@ -0,0 +1,531 @@ +@page "/events/event/{EventId:guid}/edit" +@inject IEventsService EventsService +@inject ISnackbar Snackbar +@inject IMediaFilesService MediaFilesService +@inject IIdentityService IdentityService +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Events +@using Astravent.Web.Wasm.DTO.Enums +@using Astravent.Web.Wasm.Areas.Events.CommandsDto +@using System.IO +@using System.Text.Json +@using Astravent.Web.Wasm.Utilities + + + + Edit Event + + @if (IsLoading) + { + + } + else if (eventDto == null) + { + Event not found. + } + else + { + + + + + + General Information + + + Event Description + + + + + + + + + + + + + + @foreach (Category category in Enum.GetValues(typeof(Category))) + { + @category.ToString() + } + + + + + + + + + + + Location + + + + + + + + + + + + Payment + Requires Payment + + @if (editEvent.Settings.RequiresPayment) + { + + + @foreach (PaymentMethod method in Enum.GetValues(typeof(PaymentMethod))) + { + @method.ToString() + } + + + + + + } + + + + + Media and Visibility + + + + + + + + Additional Settings + Requires Approval + Is Online Event + Is Private + Requires RSVP + Allows Guests + Show Attendees Publicly + Send Reminders + + Enable Chat + Allow Comments + Issue Tickets + Record Event + + + + + + + Submit + Save Changes + + + + } + + + + + + + + +@code { + [Parameter] public Guid EventId { get; set; } + + private EventDto eventDto; + private UpdateEventCommand editEvent = new UpdateEventCommand(); + private MudForm form; + + private string bannerPreviewUrl; + private List mediaFilesPreviews = new(); + private string CroppedImageBase64; + + private IBrowserFile croppedImageFile; + private IReadOnlyList mediaFiles; + private bool IsLoading { get; set; } = true; + private bool IsUploading { get; set; } = false; + private string currentImageType = string.Empty; + + private string defaultBannerImage = "/images/default_banner_image.png"; + + private DateTime? startDateTemp; + private TimeSpan? startTimeTemp; + private DateTime? endDateTemp; + private TimeSpan? endTimeTemp; + private DateTime? publishDateTemp; + private TimeSpan? publishTimeTemp; + + protected override async Task OnInitializedAsync() + { + IsLoading = true; + + try + { + // Initialize the authentication state + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + eventDto = await EventsService.GetEventAsync(EventId); + + if (eventDto != null) + { + editEvent = new UpdateEventCommand + { + EventId = eventDto.Id, + Name = eventDto.Name ?? string.Empty, + Description = eventDto.Description ?? string.Empty, + StartDate = eventDto.StartDate.ToString("yyyy-MM-ddTHH:mm:ss"), + EndDate = eventDto.EndDate.ToString("yyyy-MM-ddTHH:mm:ss"), + Category = eventDto.Category, + Capacity = eventDto.Capacity, + PublishDate = eventDto.PublishDate.ToString("yyyy-MM-ddTHH:mm:ss"), + Visibility = Enum.Parse(eventDto.Status ?? EventVisibility.Public.ToString()), + BannerUrl = eventDto.BannerUrl ?? string.Empty, + Settings = eventDto.Settings ?? new EventSettings(), + MediaFilesUrl = eventDto.MediaFilesUrl?.ToList() ?? new List(), + BuildingName = eventDto.Location?.BuildingName ?? string.Empty, + Street = eventDto.Location?.Street ?? string.Empty, + BuildingNumber = eventDto.Location?.BuildingNumber ?? string.Empty, + ApartmentNumber = eventDto.Location?.ApartmentNumber ?? string.Empty, + City = eventDto.Location?.City ?? string.Empty, + ZipCode = eventDto.Location?.ZipCode ?? string.Empty, + Country = eventDto.Location?.Country ?? string.Empty, + Fee = eventDto.Fee, + OrganizerId = eventDto.Organizer?.Id ?? Guid.Empty, + OrganizationId = eventDto.Organizer?.OrganizationId ?? Guid.Empty + }; + + startDateTemp = eventDto.StartDate; + startTimeTemp = eventDto.StartDate.TimeOfDay; + endDateTemp = eventDto.EndDate; + endTimeTemp = eventDto.EndDate.TimeOfDay; + publishDateTemp = eventDto.PublishDate; + publishTimeTemp = eventDto.PublishDate.TimeOfDay; + + bannerPreviewUrl = editEvent.BannerUrl; + mediaFilesPreviews = eventDto.MediaFilesUrl?.Select(url => new FilePreview + { + Url = url, + IsImage = url.EndsWith(".jpg") || url.EndsWith(".png"), + IsVideo = url.EndsWith(".mp4"), + IsPdf = url.EndsWith(".pdf") + }).ToList() ?? new List(); + } + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load event: {ex.Message}", Severity.Error); + } + finally + { + IsLoading = false; + } + } + + private async Task UploadBannerClick() + { + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputBanner').click()"); + } + + private async Task UploadMediaFilesClick() + { + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputMediaFiles').click()"); + } + + private async Task OpenCropper(InputFileChangeEventArgs e, string imageType) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageType = imageType; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, imageType); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); + + try + { + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + string imageType = "EventBanner"; + var response = await MediaFilesService.UploadMediaFileAsync( + sourceId: editEvent.EventId, + sourceType: imageType, + uploaderId: IdentityService.GetCurrentUserId(), + fileName: $"{editEvent.Name}_{currentImageType}.png", + fileContentType: croppedImageFile.ContentType, + fileData: fileData + ); + + var responseDto = response?.Content; + + if (response.IsSuccessStatusCode && responseDto != null) + { + var uploadedUrl = responseDto.ProcessedUrl; + if (!string.IsNullOrWhiteSpace(uploadedUrl)) + { + bannerPreviewUrl = uploadedUrl; + editEvent.BannerUrl = bannerPreviewUrl; + StateHasChanged(); + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Uploaded URL is null or empty."); + } + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Failed to upload the image."); + } + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + finally + { + IsUploading = false; + StateHasChanged(); + } + + CloseCropper(); + } + } + + private async Task UploadMediaFiles(InputFileChangeEventArgs e) + { + const long maxAllowedSize = 10 * 1024 * 1024; + mediaFiles = e.GetMultipleFiles(); + + foreach (var file in mediaFiles) + { + if (file.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + } + + foreach (var file in mediaFiles) + { + using var stream = file.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + + var response = await MediaFilesService.UploadFileAsync( + sourceId: editEvent.EventId, + sourceType: "EventFile", + uploaderId: IdentityService.GetCurrentUserId(), + fileName: file.Name, + fileContentType: file.ContentType, + fileData: buffer + ); + + var responseDto = response.Content; + + if (response.IsSuccessStatusCode && responseDto != null) + { + var uploadedUrl = responseDto.FileUrl; + if (!string.IsNullOrWhiteSpace(uploadedUrl)) + { + mediaFilesPreviews.Add(new FilePreview + { + Url = uploadedUrl, + ContentType = file.ContentType, + IsImage = file.ContentType.StartsWith("image/"), + IsVideo = file.ContentType.StartsWith("video/"), + IsPdf = file.ContentType.EndsWith("pdf") + }); + editEvent.MediaFilesUrl = editEvent.MediaFilesUrl.Append(uploadedUrl).ToList(); + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Uploaded URL is null or empty."); + } + } + else + { + await JSRuntime.InvokeVoidAsync("alert", $"Failed to upload {file.Name}."); + } + } + + StateHasChanged(); + } + + private async Task SubmitEvent() + { + if (startDateTemp.HasValue && startTimeTemp.HasValue) + { + editEvent.StartDate = (startDateTemp.Value.Date + startTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (endDateTemp.HasValue && endTimeTemp.HasValue) + { + editEvent.EndDate = (endDateTemp.Value.Date + endTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + if (publishDateTemp.HasValue && publishTimeTemp.HasValue) + { + editEvent.PublishDate = (publishDateTemp.Value.Date + publishTimeTemp.Value).ToString("yyyy-MM-ddTHH:mm:ss"); + } + + await form.Validate(); + if (form.IsValid) + { + var response = await EventsService.UpdateEventAsync(editEvent); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Event updated successfully!", Severity.Success); + NavigationManager.NavigateTo($"/events/event/{editEvent.EventId}"); + } + else + { + var errorMessage = "Failed to update event. Please try again."; + if (response.ErrorMessage != null) + { + errorMessage = $"Error: {response.ErrorMessage.Reason}"; + } + + Snackbar.Add(errorMessage, Severity.Error); + } + } + } + + private string GetBannerUrl() + { + return !string.IsNullOrWhiteSpace(bannerPreviewUrl) ? bannerPreviewUrl : defaultBannerImage; + } + + private class FilePreview + { + public string Url { get; set; } + public string ContentType { get; set; } + public bool IsImage { get; set; } + public bool IsVideo { get; set; } + public bool IsPdf { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EditOrganization.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EditOrganization.razor new file mode 100644 index 000000000..e85478929 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EditOrganization.razor @@ -0,0 +1,446 @@ +@page "/organizations/edit/{OrganizationId:guid}" +@inject IOrganizationsService OrganizationsService +@inject IIdentityService IdentityService +@inject IMediaFilesService MediaFilesService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime +@inject ISnackbar Snackbar +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO.Enums +@using MudBlazor +@using System.IO +@using Astravent.Web.Wasm.Utilities +@using System.Text.Json + + + + @if (isLoading || IsUploading) + { +
+ + Uploading Image, please wait... +
+ } + else if (organizationGalleryUsers == null) + { + Failed to load organization details. + } + else + { + + +
+ +
+ +
+ +
+ + + @organizationGalleryUsers.OrganizationDetails.Name + + + + + + Save + + +
+ + + + + Overview + Settings + Members + Roles + Gallery + + + + + @if (selectedTabIndex == 0) + { + Edit Overview + + + + + + + + + @foreach (var role in organizationGalleryUsers.OrganizationDetails.Roles) + { + @role.Name + } + + } + else if (selectedTabIndex == 1) + { + Organization Settings + @if (organizationGalleryUsers?.OrganizationDetails?.Settings != null) + { + + } + else + { + Settings are not available. + } + } + else if (selectedTabIndex == 2) + { + Manage Members + } + else if (selectedTabIndex == 3) + { + Manage Roles + + } + else if (selectedTabIndex == 4) + { + Manage Organization Gallery + + } + + + + + } +
+
+ + + + +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private OrganizationGalleryUsersDto organizationGalleryUsers; + private bool isLoading = true; + private bool IsUploading { get; set; } = false; + + private int selectedTabIndex = 0; + private string CroppedImageBase64 { get; set; } + + private IBrowserFile croppedImageFile; + private string currentImageType = string.Empty; // "profile" or "banner" + + private string defaultBannerImage = "/images/default_banner_image.png"; + private string defaultProfileImage = "/images/default_organization_profile_image.png"; + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", DotNetObjectReference.Create(this)); + } + } + + protected override async Task OnInitializedAsync() + { + isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + organizationGalleryUsers = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + Console.WriteLine(JsonSerializer.Serialize(organizationGalleryUsers)); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + organizationGalleryUsers = null; + } + finally + { + isLoading = false; + } + } + + private string GetBannerUrl() + { + return !string.IsNullOrWhiteSpace(organizationGalleryUsers?.OrganizationDetails?.BannerUrl) + ? organizationGalleryUsers.OrganizationDetails.BannerUrl + : defaultBannerImage; + } + + private string GetProfileImageUrl() + { + return !string.IsNullOrWhiteSpace(organizationGalleryUsers?.OrganizationDetails?.ImageUrl) + ? organizationGalleryUsers.OrganizationDetails.ImageUrl + : defaultProfileImage; + } + + private void LoadTabContent(int index) + { + selectedTabIndex = index; + } + + private async Task OpenCropper(InputFileChangeEventArgs e, string imageType) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageType = imageType; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, imageType); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); // Trigger UI update to show the overlay + + try + { + // Convert IBrowserFile to byte[] + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + string imageType = currentImageType == "profile" ? "OrganizationProfileImage" : "OrganizationBannerImage"; + var response = await MediaFilesService.UploadOrganizationImageAsync( + OrganizationId, + imageType, + IdentityService.GetCurrentUserId(), + $"{organizationGalleryUsers.OrganizationDetails.Name}_{currentImageType}.png", + croppedImageFile.ContentType, + fileData); + + if (response.IsSuccessStatusCode) + { + var uploadedFile = response.Content; + if (uploadedFile != null) + { + if (currentImageType == "profile") + { + organizationGalleryUsers.OrganizationDetails.ImageUrl = uploadedFile.ProcessedUrl; + } + else + { + organizationGalleryUsers.OrganizationDetails.BannerUrl = uploadedFile.ProcessedUrl; + } + + StateHasChanged(); + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + } + else + { + await JSRuntime.InvokeVoidAsync("alert", "Failed to upload the image."); + } + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + finally + { + IsUploading = false; + StateHasChanged(); + } + + CloseCropper(); + } + } + + private async Task RemoveImage(string imageType) + { + try + { + string imageUrl = imageType == "profile" ? organizationGalleryUsers.OrganizationDetails.ImageUrl : organizationGalleryUsers.OrganizationDetails.BannerUrl; + await MediaFilesService.DeleteMediaFileAsync(imageUrl); + + if (imageType == "profile") + { + organizationGalleryUsers.OrganizationDetails.ImageUrl = null; + } + else + { + organizationGalleryUsers.OrganizationDetails.BannerUrl = null; + } + + StateHasChanged(); + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred while removing the image: {ex.Message}"); + } + } + + private async Task SaveOrganization() + { + var organizationDetails = organizationGalleryUsers.OrganizationDetails; + + var parentId = organizationDetails.ParentOrganizationId ?? Guid.Empty; + + var updateCommand = new UpdateOrganizationCommand( + OrganizationId, + organizationDetails.Name, + organizationDetails.Description, + Guid.Empty, + parentId, + organizationDetails.OwnerId, + organizationDetails.Settings, + organizationDetails.BannerUrl, + organizationDetails.ImageUrl, + organizationDetails.DefaultRoleName, + organizationDetails.Address, + organizationDetails.Country, + organizationDetails.City, + organizationDetails.Telephone, + organizationDetails.Email + ); + + var response = await OrganizationsService.UpdateOrganizationAsync(OrganizationId, updateCommand); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Organization updated successfully.", Severity.Success); + NavigationManager.NavigateTo($"/organizations/details/{OrganizationId}"); + } + else + { + Snackbar.Add("Failed to update organization. Please try again.", Severity.Error); + } + } + + private async Task HandleSaveSettings(OrganizationSettingsDto settings) + { + var updateCommand = new UpdateOrganizationSettingsCommand(OrganizationId, settings); + + var response = await OrganizationsService.UpdateOrganizationSettingsAsync(OrganizationId, updateCommand); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Settings updated successfully.", Severity.Success); + } + else + { + Snackbar.Add("Failed to update settings. Please try again.", Severity.Error); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EventsComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EventsComponent.razor new file mode 100644 index 000000000..c3d2c3452 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/EventsComponent.razor @@ -0,0 +1,224 @@ +@page "/organizations/{OrganizationId}/events" +@inject IEventsService EventsService +@inject NavigationManager NavigationManager +@inject IIdentityService IdentityService +@inject IOrganizationsService OrganizationsService +@inject ISnackbar Snackbar +@inject IDialogService DialogService +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Events +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO.Enums +@using Astravent.Web.Wasm.Areas.Events.CommandsDto +@using Astravent.Web.Wasm.DTO.Wrappers + + + + Events + @if (canPostEvents) + { + + Create Event + + } + + + + + @if (isLoading) + { + + } + else if (events != null && events.Any()) + { + + @foreach (var eventDto in events) + { + + + + + @eventDto.Name + @eventDto.Category + Starts: @eventDto.StartDate.ToString("MMMM dd, yyyy") + Ends: @eventDto.EndDate.ToString("MMMM dd, yyyy") + + + + View Details + + @if (canEditEvent(eventDto)) + { + + Edit + + } + @if (canDeleteEvent()) + { + + Delete + + } + + + + } + + } + else + { + No events found. + } + + + + +@code { + [Parameter] public Guid OrganizationId { get; set; } + private List events; + private bool isLoading = true; + private bool canPostEvents = false; + private bool isAdmin = false; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + var organization = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + canPostEvents = organization.OrganizationDetails.Settings?.CanPostEvents ?? false; + isAdmin = CheckIfUserIsAdmin(organization.OrganizationDetails); + await LoadEvents(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); // Log to console for debugging + Snackbar.Add($"Failed to load events: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task LoadEvents() + { + try + { + var searchCommand = new SearchEvents + { + OrganizationId = OrganizationId, + Pageable = new PageableDto + { + Page = 1, + Size = 50 + } + }; + + var result = await EventsService.SearchEventsAsync(searchCommand); + + // Debugging: Log result to ensure data is returned + Console.WriteLine($"Events fetched: {result?.Items.Count()}"); + + events = result?.Items.ToList() ?? new List(); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); // Log to console for debugging + Snackbar.Add($"Failed to load events: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private bool CheckIfUserIsAdmin(OrganizationDetailsDto organization) + { + var currentUserId = IdentityService.GetCurrentUserId(); + return organization.OwnerId == currentUserId || organization.Users?.Any(u => u.Id == currentUserId && u.Role.Permissions.ContainsKey(Permission.CreateEvents)) == true; + } + + private bool canEditEvent(EventDto eventDto) + { + var currentUserId = IdentityService.GetCurrentUserId(); + return isAdmin || eventDto.Organizer?.UserId == currentUserId || HasPermission(Permission.EditEvents, OrganizationId, currentUserId); + } + + private bool canDeleteEvent() + { + var currentUserId = IdentityService.GetCurrentUserId(); + return isAdmin || HasPermission(Permission.DeleteEvents, OrganizationId, currentUserId); + } + + private bool HasPermission(Permission permission, Guid organizationId, Guid userId) + { + var organization = OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(organizationId).Result; + var user = organization.Users?.FirstOrDefault(u => u.Id == userId); + return user?.Role.Permissions.ContainsKey(permission) == true; + } + + private void CreateEvent() + { + if (!canPostEvents) + { + Snackbar.Add("You do not have permission to create events for this organization.", Severity.Warning); + return; + } + + NavigationManager.NavigateTo($"/organizations/{OrganizationId}/events/create"); + } + + private void ViewEvent(Guid eventId) + { + NavigationManager.NavigateTo($"/events/event/{eventId}"); + } + + private void EditEvent(Guid eventId) + { + NavigationManager.NavigateTo($"/events/event/{eventId}/edit"); + } + + private async Task DeleteEvent(Guid eventId) + { + if (!canDeleteEvent()) + { + Snackbar.Add("You do not have permission to delete this event.", Severity.Warning); + return; + } + + bool? confirmed = await DialogService.ShowMessageBox( + "Confirm Delete", + "Are you sure you want to delete this event?", + yesText: "Yes", cancelText: "No"); + + if (confirmed == true) + { + try + { + await EventsService.DeleteEventAsync(eventId); + Snackbar.Add("Event deleted successfully.", Severity.Success); + await LoadEvents(); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to delete event: {ex.Message}", Severity.Error); + } + } + } + + private string GetBannerUrl(EventDto eventDto) + { + return !string.IsNullOrWhiteSpace(eventDto.BannerUrl) ? eventDto.BannerUrl : "/images/default_media_file_image.png"; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/MyOrganizations.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/MyOrganizations.razor new file mode 100644 index 000000000..29b12e66a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/MyOrganizations.razor @@ -0,0 +1,162 @@ +@page "/organizations/my" +@inject IOrganizationsService OrganizationsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@inject IIdentityService IdentityService +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Organizations +@using System.Collections.Generic +@using System.Linq +@using System.Threading.Tasks + + + + My Organizations + + + + Create Organization + + + View Tree + + + + @if (_isLoading) + { + + } + else if (_organizations != null && _organizations.Any()) + { + @foreach (var organization in _organizations) + { + + + + + + @organization.Name + @organization.Description + Users: @organization.UserCount + + + + View + + + + + } + } + else + { + No organizations found. + } + + + + + + + + Page @_currentPage of @_totalPages + + + + + +@code { + private bool _isLoading = true; + private List _organizations = new List(); + private int _currentPage = 1; + private int _pageSize = 10; + private int _totalPages; + private int _totalItems; + + // Define pageSizeOptions array + private readonly int[] pageSizeOptions = { 6, 10, 15, 20 }; + + protected override async Task OnInitializedAsync() + { + _isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await LoadOrganizationsAsync(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load organizations: {ex.Message}", Severity.Error); + } + finally + { + _isLoading = false; + } + } + + private async Task LoadOrganizationsAsync() + { + try + { + var userId = IdentityService.GetCurrentUserId(); + var pagedOrganizations = await OrganizationsService.GetPaginatedUserOrganizationsAsync(userId, _currentPage, _pageSize); + + _organizations = pagedOrganizations?.Items?.ToList() ?? new List(); + _totalPages = pagedOrganizations?.TotalPages ?? 0; + _totalItems = pagedOrganizations?.TotalItems ?? 0; + } + catch (Exception ex) + { + Snackbar.Add($"Error loading organizations: {ex.Message}", Severity.Error); + } + finally + { + _isLoading = false; + } + } + + private async Task OnPageChanged(int page) + { + _currentPage = page; + await LoadOrganizationsAsync(); + } + + private string GetOrganizationImage(string imageUrl) + { + return string.IsNullOrEmpty(imageUrl) + ? "/images/default_organization_profile_image.png" + : imageUrl; + } + + private string GetOrganizationBanner(string bannerUrl) + { + return string.IsNullOrEmpty(bannerUrl) + ? "/images/default_banner_image.png" + : bannerUrl; + } + + private void CreateOrganization() + { + NavigationManager.NavigateTo("/organizations/create"); + } + + private void NavigateToOrganization(Guid organizationId) + { + NavigationManager.NavigateTo($"/organizations/details/{organizationId}"); + } + + private void ViewTree() + { + NavigationManager.NavigateTo("/organizations/tree"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationDetails.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationDetails.razor new file mode 100644 index 000000000..8af97b7bb --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationDetails.razor @@ -0,0 +1,201 @@ +@page "/organizations/details/{OrganizationId:guid}" +@inject IOrganizationsService OrganizationsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO.Enums +@using MudBlazor + + + + @if (isLoading) + { + + } + else if (organizationGalleryUsers == null) + { + Failed to load organization details. + } + else + { + + + + + +
+ +
+ + + @organizationGalleryUsers.OrganizationDetails.Name + + + + + + @if (isAdmin) + { + + Edit + + } + +
+ + + + + + + Feed + Overview + Posts + Events + Members + Requests + Suborganizations + Gallery + + + + + + + @if (selectedTabIndex == 1) + { + + } + else if (selectedTabIndex == 2) + { + + } + else if (selectedTabIndex == 3) + { + + } + else if (selectedTabIndex == 4) + { + + } + else if (selectedTabIndex == 5) + { + + } + else if (selectedTabIndex == 6) + { + + } + else if (selectedTabIndex == 7) + { + + } + + + + } +
+
+ +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private OrganizationGalleryUsersDto organizationGalleryUsers; + private bool isAdmin; + private bool isLoading = true; + private int selectedTabIndex = 1; + + private string defaultBannerImage = "/images/default_banner_image.png"; + private string defaultProfileImage = "/images/default_organization_profile_image.png"; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + organizationGalleryUsers = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + isAdmin = CheckIfUserIsAdminOrHasPermissions(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + organizationGalleryUsers = null; + } + finally + { + isLoading = false; + } + } + + private bool CheckIfUserIsAdminOrHasPermissions() + { + var currentUserId = IdentityService.GetCurrentUserId(); + + if (organizationGalleryUsers?.OrganizationDetails?.OwnerId == currentUserId) + { + return true; + } + + var currentUserRole = organizationGalleryUsers?.Users? + .FirstOrDefault(u => u.Id == currentUserId)?.Role; + + if (currentUserRole != null) + { + return currentUserRole.Permissions.TryGetValue(Permission.EditOrganizationDetails, out bool canEdit) && canEdit || + currentUserRole.Permissions.TryGetValue(Permission.UpdateOrganizationImage, out bool canUpdateImage) && canUpdateImage; + } + + return false; + } + + private string GetBannerUrl() + { + return !string.IsNullOrWhiteSpace(organizationGalleryUsers?.OrganizationDetails?.BannerUrl) + ? organizationGalleryUsers.OrganizationDetails.BannerUrl + : defaultBannerImage; + } + + private string GetProfileImageUrl() + { + return !string.IsNullOrWhiteSpace(organizationGalleryUsers?.OrganizationDetails?.ImageUrl) + ? organizationGalleryUsers.OrganizationDetails.ImageUrl + : defaultProfileImage; + } + + private void LoadTabContent(int index) + { + selectedTabIndex = index; + } + + private void NavigateToFeed() + { + NavigationManager.NavigateTo($"/organizations/{OrganizationId}/feed"); + } + private void EditOrganization() + { + NavigationManager.NavigateTo($"/organizations/edit/{OrganizationId}"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationGallery.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationGallery.razor new file mode 100644 index 000000000..6f80f4138 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationGallery.razor @@ -0,0 +1,242 @@ +@page "/organizations/{OrganizationId:guid}/gallery" +@inject IOrganizationsService OrganizationsService +@inject IIdentityService IdentityService +@inject IMediaFilesService MediaFilesService +@inject IJSRuntime JSRuntime +@using Astravent.Web.Wasm.Utilities +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Organizations +@using System.IO +@using MudBlazor + +Organization Gallery +@if (IsLoading) +{ + +} +else +{ + +
+ + Upload New Image + +
+ + @if (GalleryImages == null || !GalleryImages.Any()) + { + No images found in the gallery. + } + else + { + + + @foreach (var image in GalleryImages) + { + + + + } + + } +} + + + + +@if (!string.IsNullOrEmpty(CroppedImageBase64)) +{ +
+ Cropped Image Preview: + +
+} + +@code { + [Parameter] public Guid OrganizationId { get; set; } + private List GalleryImages { get; set; } + private bool IsLoading { get; set; } = true; + private bool IsUploading { get; set; } = false; + private string CroppedImageBase64 { get; set; } + private IBrowserFile croppedImageFile; + private Guid currentImageId; + + protected override async Task OnInitializedAsync() + { + try + { + IsLoading = true; + + // Fetch organization details along with gallery and users + var organizationGalleryUsers = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + GalleryImages = organizationGalleryUsers?.Gallery?.ToList() ?? new List(); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + GalleryImages = null; + } + finally + { + IsLoading = false; + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", DotNetObjectReference.Create(this)); + } + } + + private async Task OpenCropper(InputFileChangeEventArgs e, Guid imageId) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageId = imageId; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, "profile"); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); + + try + { + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + var response = await MediaFilesService.UploadOrganizationImageAsync( + OrganizationId, + "OrganizationGalleryImage", + IdentityService.GetCurrentUserId(), + $"gallery_image_{currentImageId}.png", + croppedImageFile.ContentType, + fileData); + + // Refresh the gallery images after successful upload + if (response.IsSuccessStatusCode) + { + var organizationGalleryUsers = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + GalleryImages = organizationGalleryUsers?.Gallery?.ToList() ?? new List(); + } + + StateHasChanged(); + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + CloseCropper(); + } + } + + private async Task RemoveImage(Guid imageId) + { + IsUploading = true; + StateHasChanged(); + + try + { + var imageUrl = GalleryImages.FirstOrDefault(img => img.ImageId == imageId)?.ImageUrl; + if (!string.IsNullOrEmpty(imageUrl)) + { + await MediaFilesService.DeleteMediaFileAsync(imageUrl); + GalleryImages.RemoveAll(img => img.ImageId == imageId); + StateHasChanged(); + } + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred while removing the image: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationGalleryComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationGalleryComponent.razor new file mode 100644 index 000000000..154f3224c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationGalleryComponent.razor @@ -0,0 +1,276 @@ +@page "/organizations/details/{OrganizationId:guid}/gallery" +@inject IOrganizationsService OrganizationsService +@inject IIdentityService IdentityService +@inject IMediaFilesService MediaFilesService +@inject IJSRuntime JSRuntime +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.Utilities +@using Astravent.Web.Wasm.DTO.Enums +@using MudBlazor +@using System.IO + +Organization Gallery +@if (IsLoading) +{ + +} +else +{ + @if (CanManageGallery) + { + +
+ + Upload New Image + +
+ } + + @if (GalleryImages == null || !GalleryImages.Any()) + { + No images found in the gallery. + } + else + { + + + @foreach (var image in GalleryImages) + { + + + + } + + } +} + + + + + +@if (!string.IsNullOrEmpty(CroppedImageBase64)) +{ +
+ Cropped Image Preview: + +
+} + +@code { + [Parameter] public Guid OrganizationId { get; set; } + private List GalleryImages { get; set; } + private bool IsLoading { get; set; } = true; + private bool IsUploading { get; set; } = false; + private string CroppedImageBase64 { get; set; } + private IBrowserFile croppedImageFile; + private Guid currentImageId; + private bool CanManageGallery { get; set; } = false; + + protected override async Task OnInitializedAsync() + { + try + { + IsLoading = true; + + // Fetch organization details along with gallery and users + var organizationGalleryUsers = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + GalleryImages = organizationGalleryUsers?.Gallery?.ToList() ?? new List(); + + // Check if the current user has permission to manage the gallery + CanManageGallery = CheckIfUserCanManageGallery(organizationGalleryUsers); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + GalleryImages = null; + } + finally + { + IsLoading = false; + } + } + + private bool CheckIfUserCanManageGallery(OrganizationGalleryUsersDto organizationGalleryUsers) + { + var currentUserId = IdentityService.GetCurrentUserId(); + + // Check if the current user is the owner or has specific permissions + if (organizationGalleryUsers?.OrganizationDetails?.OwnerId == currentUserId) + { + return true; + } + + var currentUserRole = organizationGalleryUsers?.Users? + .FirstOrDefault(u => u.Id == currentUserId)?.Role; + + if (currentUserRole != null) + { + return currentUserRole.Permissions.TryGetValue(Permission.ModifyGallery, out bool canUpload) && canUpload || + currentUserRole.Permissions.TryGetValue(Permission.ModifyGallery, out bool canRemove) && canRemove; + } + + return false; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await JSRuntime.InvokeVoidAsync("GLOBAL.SetDotnetReference", DotNetObjectReference.Create(this)); + } + } + + private async Task OpenCropper(InputFileChangeEventArgs e, Guid imageId) + { + const long maxAllowedSize = 10 * 1024 * 1024; + var inputFile = e.File; + currentImageId = imageId; + + if (inputFile != null) + { + if (inputFile.Size > maxAllowedSize) + { + await JSRuntime.InvokeVoidAsync("alert", $"File size exceeds the allowed limit of {maxAllowedSize / (1024 * 1024)} MB."); + return; + } + + using var stream = inputFile.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + var base64Image = Convert.ToBase64String(buffer); + await JSRuntime.InvokeVoidAsync("displayImageAndInitializeCropper", base64Image, "profile"); + } + } + + private void CloseCropper() + { + JSRuntime.InvokeVoidAsync("hideCropperModal"); + } + + [JSInvokable] + public void ReceiveCroppedImage(string base64Image) + { + if (!string.IsNullOrEmpty(base64Image)) + { + CroppedImageBase64 = $"data:image/png;base64,{base64Image}"; + var buffer = Convert.FromBase64String(base64Image); + var lastModified = DateTimeOffset.Now; + croppedImageFile = new BrowserFile(buffer, "cropped-image.png", "image/png", lastModified); + StateHasChanged(); + } + } + + private async Task SaveCroppedImage() + { + if (croppedImageFile != null) + { + IsUploading = true; + StateHasChanged(); + + try + { + byte[] fileData; + using (var stream = croppedImageFile.OpenReadStream(croppedImageFile.Size)) + using (var ms = new MemoryStream()) + { + await stream.CopyToAsync(ms); + fileData = ms.ToArray(); + } + + var response = await MediaFilesService.UploadOrganizationImageAsync( + OrganizationId, + "OrganizationGalleryImage", + IdentityService.GetCurrentUserId(), + $"gallery_image_{currentImageId}.png", + croppedImageFile.ContentType, + fileData); + + // Refresh the gallery images after successful upload + if (response.IsSuccessStatusCode) + { + var organizationGalleryUsers = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + GalleryImages = organizationGalleryUsers?.Gallery?.ToList() ?? new List(); + } + + StateHasChanged(); + NavigationManager.NavigateTo(NavigationManager.Uri, forceLoad: true); + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + CloseCropper(); + } + } + + private async Task RemoveImage(Guid imageId) + { + IsUploading = true; + StateHasChanged(); + + try + { + var imageUrl = GalleryImages.FirstOrDefault(img => img.ImageId == imageId)?.ImageUrl; + if (!string.IsNullOrEmpty(imageUrl)) + { + await MediaFilesService.DeleteMediaFileAsync(imageUrl); + GalleryImages.RemoveAll(img => img.ImageId == imageId); + StateHasChanged(); + } + } + catch (Exception ex) + { + await JSRuntime.InvokeVoidAsync("alert", $"An error occurred while removing the image: {ex.Message}"); + } + + IsUploading = false; + StateHasChanged(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationMembersComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationMembersComponent.razor new file mode 100644 index 000000000..782d88fb6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationMembersComponent.razor @@ -0,0 +1,94 @@ +@page "/organizations/{OrganizationId:guid}/members" +@inject IOrganizationsService OrganizationsService +@inject IStudentsService StudentsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using Astravent.Web.Wasm.DTO.Organizations +@using MudBlazor +@using Astravent.Web.Wasm.DTO + + + @if (isLoading) + { + + } + else if (members == null || !members.Any()) + { + No members found in this organization. + } + else + { + + @foreach (var member in members) + { + + + + + + + + + @member.FirstName @member.LastName + + + + + + View Profile + + + + + } + + } + + +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private List members; + private bool isLoading = true; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + try + { + var organizationDetails = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + if (organizationDetails?.Users != null) + { + members = new List(); + foreach (var user in organizationDetails.Users) + { + var student = await StudentsService.GetStudentAsync(user.Id); + if (student != null) + { + members.Add(student); + } + } + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add("Failed to load members.", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private string GetUserProfileImage(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "/images/default_profile_image.png" : profileImageUrl; + } + + private void NavigateToProfile(Guid memberId) + { + NavigationManager.NavigateTo($"/user-details/{memberId}"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationPosts/CreateOrganizationPost.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationPosts/CreateOrganizationPost.razor new file mode 100644 index 000000000..d710994a8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationPosts/CreateOrganizationPost.razor @@ -0,0 +1,197 @@ +@page "/organizations/{OrganizationId:guid}/posts/create" +@inject IPostsService PostsService +@inject IMediaFilesService MediaFilesService +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@inject IJSRuntime JSRuntime +@using Astravent.Web.Wasm.Areas.Posts.CommandsDto +@using Astravent.Web.Wasm.DTO.Enums.Posts +@using MudBlazor +@using System.IO + +@using Microsoft.AspNetCore.Components.Forms + + + + + Create a New Post for Organization + + + + + + + + Upload Media Files + + + + + + +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private MudForm form; + private string textContent; + private List mediaFilesPreviews = new(); + private List uploadedMediaUrls = new List(); + private string state = "InDraft"; + private string visibility = "Visible"; + private DateTime? publishDate = DateTime.Now; + private bool isUploading = false; + + private async Task UploadMediaFilesClick() + { + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputMediaFiles').click()"); + } + + private async Task UploadMediaFiles(InputFileChangeEventArgs e) + { + isUploading = true; + StateHasChanged(); + + const long maxAllowedSize = 10 * 1024 * 1024; + var files = e.GetMultipleFiles(); + + foreach (var file in files) + { + if (file.Size > maxAllowedSize) + { + Snackbar.Add($"File {file.Name} exceeds the allowed size limit.", Severity.Error); + continue; + } + + using var stream = file.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + + var response = await MediaFilesService.UploadFileAsync( + sourceId: OrganizationId, // Use OrganizationId here + sourceType: "PostFileOrganization", // Different source type for organization + uploaderId: IdentityService.GetCurrentUserId(), + fileName: file.Name, + fileContentType: file.ContentType, + fileData: buffer); + + if (response.IsSuccessStatusCode) + { + var uploadedUrl = response.Content.FileUrl; + uploadedMediaUrls.Add(uploadedUrl); + + mediaFilesPreviews.Add(new FilePreview + { + Url = uploadedUrl, + ContentType = file.ContentType, + IsImage = file.ContentType.StartsWith("image/"), + IsVideo = file.ContentType.StartsWith("video/"), + IsPdf = file.ContentType.EndsWith("pdf") + }); + } + else + { + Snackbar.Add($"Failed to upload file {file.Name}: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + isUploading = false; + StateHasChanged(); + } + + private async Task HandleSubmit() + { + if (form.IsValid) + { + var command = new CreatePostCommand + { + PostId = Guid.NewGuid(), + UserId = IdentityService.GetCurrentUserId(), + TextContent = textContent, + MediaFiles = uploadedMediaUrls.ToArray(), + State = state, + Visibility = visibility, + PublishDate = publishDate, + OrganizationId = OrganizationId, + Context = PostContext.OrganizationPage + }; + + var response = await PostsService.CreatePostAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Post created successfully.", Severity.Success); + NavigationManager.NavigateTo($"/organizations/{OrganizationId}/posts"); + } + else + { + Snackbar.Add($"Failed to create post: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + } + + private void HandleCancel() + { + NavigationManager.NavigateTo($"/organizations/{OrganizationId}/posts"); + } + + private class FilePreview + { + public string Url { get; set; } + public string ContentType { get; set; } + public bool IsImage { get; set; } + public bool IsVideo { get; set; } + public bool IsPdf { get; set; } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationPostsComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationPostsComponent.razor new file mode 100644 index 000000000..b54f62e54 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationPostsComponent.razor @@ -0,0 +1,374 @@ +@page "/organizations/{OrganizationId:guid}/posts" +@inject IOrganizationsService OrganizationsService +@inject IPostsService PostsService +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject IReactionsService ReactionsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO.Wrappers +@using Astravent.Web.Wasm.Data.Posts +@using Astravent.Web.Wasm.DTO.Enums +@using MudBlazor +@using System.Linq + + + @if (isLoading) + { + + } + else + { + @if (posts == null || !posts.Any()) + { + No posts available for this organization. + } + else + { + + @if (canCreatePosts) + { + + + Create New Post + + + } + + @foreach (var post in posts) + { + + + + + + @if (post.UserId.HasValue) + { + + } + + + + @if (post.UserId.HasValue) + { + @GetUserName(post.UserId.Value) + } + else + { + @("Unknown User") + } + + @post.CreatedAt.ToString("g") + + + + + + @if (post.MediaFiles != null && post.MediaFiles.Any()) + { + + } + + @if (reactionsSummaries.TryGetValue(post.Id, out var reactionsSummary)) + { + + @foreach (var reaction in reactionsSummary.ReactionsWithCounts.OrderByDescending(r => r.Value)) + { + + + + @reaction.Value + + + } + + + Total: @reactionsSummary.NumberOfReactions + + + + } + + + + + + + View + + + + + + React + + + + @foreach (var reactionType in Enum.GetValues(typeof(ReactionType)).Cast()) + { + + @reactionType.GetReactionText() + + } + + + + + Comment + + + + + } + + + + + + + } + } + + +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private List posts = new(); + private Dictionary studentsCache = new(); + private Dictionary reactionsSummaries = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalItems = 0; + private bool isLoading = true; + private bool canCreatePosts = false; + + protected override async Task OnInitializedAsync() + { + await LoadOrganizationPostsAsync(); + await CheckUserPermissionsAsync(); + } + + private async Task LoadOrganizationPostsAsync() + { + isLoading = true; + + try + { + var searchParams = new SearchPosts + { + OrganizationId = OrganizationId, + Pageable = new PageableDto + { + Page = currentPage, + Size = pageSize, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "desc" + } + } + }; + + var response = await PostsService.SearchPostsAsync(searchParams); + + if (response != null && response.IsSuccessStatusCode) + { + var result = response.Content; + + if (result != null && result.Items != null) + { + posts = result.Items.ToList(); + totalItems = result.TotalItems; + + foreach (var post in posts.Where(p => p.UserId.HasValue)) + { + if (!studentsCache.ContainsKey(post.UserId.Value)) + { + var student = await StudentsService.GetStudentAsync(post.UserId.Value); + if (student != null) + { + studentsCache[post.UserId.Value] = student; + } + } + + reactionsSummaries[post.Id] = await GetReactionsSummaryAsync(post.Id); + } + } + else + { + Snackbar.Add("No posts found for this organization.", Severity.Warning); + posts = new List(); + } + } + else + { + Snackbar.Add($"Failed to load posts: {response?.ErrorMessage?.Reason}", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Error: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task GetReactionsSummaryAsync(Guid postId) + { + return await ReactionsService.GetReactionsSummaryAsync(postId, ReactionContentType.Post); + } + + private string GetUserAvatar(Guid userId) + { + if (studentsCache.ContainsKey(userId)) + { + return studentsCache[userId].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid userId) + { + if (studentsCache.ContainsKey(userId)) + { + return $"{studentsCache[userId].FirstName} {studentsCache[userId].LastName}"; + } + return "Unknown User"; + } + + private async Task OnPageChanged(int newPage) + { + currentPage = newPage; + await LoadOrganizationPostsAsync(); + } + + private void NavigateToPostDetails(Guid postId) + { + NavigationManager.NavigateTo($"/organizations/{OrganizationId}/posts/{postId}"); + } + + private void CreateNewPost() + { + NavigationManager.NavigateTo($"/organizations/{OrganizationId}/posts/create"); + } + + private async Task HandleReactionAsync(PostDto post, ReactionType reactionType) + { + var existingReaction = await GetExistingReactionAsync(post.Id); + + string targetType = post.OrganizationId.HasValue ? "Organization" : "User"; + + if (existingReaction != null) + { + var updateReaction = new UpdateReactionDto + { + ReactionId = existingReaction.Id, + UserId = IdentityService.UserDto.Id, + NewReactionType = reactionType.ToString() + }; + + var updateResult = await ReactionsService.UpdateReactionAsync(updateReaction); + + if (updateResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction updated successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to update reaction: {updateResult.ErrorMessage?.Reason}", Severity.Error); + } + } + else + { + var createReaction = new CreateReactionDto + { + UserId = IdentityService.UserDto.Id, + ContentId = post.Id, + ContentType = "Post", + ReactionType = reactionType.ToString(), + TargetType = targetType + }; + + var createResult = await ReactionsService.CreateReactionAsync(createReaction); + + if (createResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction added successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to add reaction: {createResult.ErrorMessage?.Reason}", Severity.Error); + } + } + + await LoadOrganizationPostsAsync(); + } + + private async Task GetExistingReactionAsync(Guid postId) + { + var reactions = await ReactionsService.GetReactionsAsync(postId, ReactionContentType.Post); + return reactions.FirstOrDefault(r => r.UserId == IdentityService.UserDto.Id); + } + + private async Task CheckUserPermissionsAsync() + { + try + { + var currentUserId = IdentityService.GetCurrentUserId(); + + var organizationDetails = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + + if (organizationDetails == null || organizationDetails.OrganizationDetails == null) + { + Snackbar.Add("Failed to load organization details.", Severity.Error); + return; + } + + if (organizationDetails.Users == null || !organizationDetails.Users.Any()) + { + Snackbar.Add("No users found in this organization.", Severity.Error); + return; + } + + var currentUserRole = organizationDetails.Users.FirstOrDefault(u => u.Id == currentUserId)?.Role; + + if (currentUserRole == null) + { + Snackbar.Add("User role not found in this organization.", Severity.Warning); + return; + } + + var organizationRole = organizationDetails.OrganizationDetails.Roles.FirstOrDefault(r => r.Id == currentUserRole.Id); + + if (organizationRole == null) + { + Snackbar.Add("User's role is not defined in the organization roles.", Severity.Error); + return; + } + + if (organizationRole.Permissions != null && organizationRole.Permissions.TryGetValue(Permission.MakePosts, out bool canPost) && canPost) + { + canCreatePosts = true; + Console.WriteLine("User has permission to create posts."); + } + else + { + Snackbar.Add("User does not have permission to create posts.", Severity.Warning); + Console.WriteLine("User does NOT have permission to create posts."); + } + + StateHasChanged(); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to check user permissions: {ex.Message}", Severity.Error); + Console.WriteLine($"Exception in CheckUserPermissionsAsync: {ex.Message}"); + } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationRequestsComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationRequestsComponent.razor new file mode 100644 index 000000000..11b768e25 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationRequestsComponent.razor @@ -0,0 +1,164 @@ +@page "/organizations/{OrganizationId:guid}/requests" +@using MudBlazor +@inject ISnackbar Snackbar +@inject IOrganizationsService OrganizationsService +@inject IStudentsService StudentsService + +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private List requests = new List(); + private Dictionary users = new Dictionary(); + private bool isLoading = true; + private int page = 1; + private int pageSize = 10; + private int totalItems; + + protected override async Task OnInitializedAsync() + { + await LoadRequests(); + } + + private async Task LoadRequests() + { + isLoading = true; + try + { + var result = await OrganizationsService.GetOrganizationRequestsAsync(OrganizationId, page, pageSize); + if (result != null) + { + requests = result.Items.ToList(); + totalItems = result.TotalItems; + + foreach (var request in requests) + { + if (!users.ContainsKey(request.UserId)) + { + var user = await StudentsService.GetStudentAsync(request.UserId); + if (user != null) + { + users[request.UserId] = user; + } + } + } + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add("Failed to load organization requests.", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task AcceptRequest(Guid requestId) + { + try + { + await OrganizationsService.AcceptFollowRequestAsync(OrganizationId, requestId); + Snackbar.Add("Request accepted successfully.", Severity.Success); + await LoadRequests(); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add("Failed to accept request.", Severity.Error); + } + } + + private async Task RejectRequest(Guid requestId) + { + try + { + await OrganizationsService.RejectFollowRequestAsync(OrganizationId, requestId, "Request rejected by admin."); + Snackbar.Add("Request rejected successfully.", Severity.Success); + await LoadRequests(); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add("Failed to reject request.", Severity.Error); + } + } + + private void OnPageChanged(int newPage) + { + page = newPage + 1; // Adjust to handle MudTablePager's zero-based index + _ = LoadRequests(); + } + + private string GetUserAvatar(Guid userId) + { + return users.ContainsKey(userId) && !string.IsNullOrEmpty(users[userId]?.ProfileImageUrl) + ? users[userId].ProfileImageUrl + : "/images/default_profile_image.png"; + } +} + + + @if (isLoading) + { + + } + else if (requests == null || !requests.Any()) + { + No requests found for this organization. + } + else + { + + @foreach (var request in requests) + { + + + + + + + + + + + + @users[request.UserId]?.FirstName @users[request.UserId]?.LastName + + + Requested on @request.RequestDate.ToString("g") + + + @request.State + + + Reason: @request.Reason + + + + + + + + Accept + + + Reject + + + + + + + + } + + + + } + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationSettings.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationSettings.razor new file mode 100644 index 000000000..558106a56 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationSettings.razor @@ -0,0 +1,71 @@ +@using Astravent.Web.Wasm.DTO.Organizations +@using MudBlazor + +@if (isSaving) +{ +
+ + Saving... +
+} + +@if (Settings != null) +{ + + + + + + + + + + + + + + + + + Save Settings + + +} +else +{ + Settings are not available. +} + +@code { + [Parameter] + public OrganizationSettingsDto Settings { get; set; } + + [Parameter] + public EventCallback OnSave { get; set; } + + private MudForm form; + private bool isSaving = false; + + private async Task SaveSettings() + { + await form.Validate(); + + if (form.IsValid) + { + isSaving = true; + try + { + await OnSave.InvokeAsync(Settings); + } + catch (Exception ex) + { + // Handle exceptions and notify the user + Console.Error.WriteLine(ex.Message); + } + finally + { + isSaving = false; + } + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationsFollowing.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationsFollowing.razor new file mode 100644 index 000000000..0fecae3e9 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationsFollowing.razor @@ -0,0 +1,128 @@ +@page "/organizations/following" +@inject IOrganizationsService OrganizationsService +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Organizations +@using System.Threading.Tasks + + + + + Organizations You're Following + + + + + @if (_organizations != null && _organizations.Any()) + { + @foreach (var organization in _organizations) + { + + + + + + @organization.OrganizationDetails.Name + @organization.OrganizationDetails.Description + Users: @organization.Users.Count() + + + + View + + + + + } + } + else if (_organizations == null) + { + Loading followed organizations... + } + else + { + No followed organizations found. + } + + + + + +@code { + private string _searchQuery = string.Empty; + private IEnumerable _organizations; + private bool _isLoading = true; + + protected override async Task OnInitializedAsync() + { + try + { + _isLoading = true; + + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await SearchOrganizations(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error during initialization: {ex.Message}"); + } + finally + { + _isLoading = false; + } + } + + private async Task SearchOrganizations() + { + try + { + _isLoading = true; + var followedOrganizations = await OrganizationsService.GetUserFollowedOrganizationsAsync(IdentityService.UserDto.Id); + + if (!string.IsNullOrEmpty(_searchQuery)) + { + _organizations = followedOrganizations.Where(o => o.OrganizationDetails.Name.Contains(_searchQuery, StringComparison.OrdinalIgnoreCase)); + } + else + { + _organizations = followedOrganizations; + } + + _isLoading = false; + StateHasChanged(); + } + catch (Exception ex) + { + Console.WriteLine($"Error fetching followed organizations: {ex.Message}"); + _isLoading = false; + } + } + + private void NavigateToOrganization(Guid organizationId) + { + NavigationManager.NavigateTo($"/organizations/details/{organizationId}"); + } + + private string GetOrganizationImage(string imageUrl) + { + return string.IsNullOrEmpty(imageUrl) + ? "/images/default_organization_profile_image.png" + : imageUrl; + } + + private string GetOrganizationBanner(string bannerUrl) + { + return string.IsNullOrEmpty(bannerUrl) + ? "/images/default_banner_image.png" + : bannerUrl; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationsSearch.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationsSearch.razor new file mode 100644 index 000000000..266ae9218 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OrganizationsSearch.razor @@ -0,0 +1,172 @@ +@page "/organizations/search" +@inject IOrganizationsService OrganizationsService +@inject NavigationManager NavigationManager +@inject IIdentityService IdentityService +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Organizations +@using System.Threading.Tasks + + + + + Discover Organizations + + + + + @if (_organizations != null && _organizations.Items != null && _organizations.Items.Any()) + { + @foreach (var organization in _organizations.Items) + { + + + + + + @organization.Name + @organization.Description + Users: @organization.UserCount + + + + View + + @if (IsUserMemberOfOrganization(organization)) + { + + Member + + } + else + { + + Follow + + } + + + + } + } + else if (_organizations == null || _organizations.Items == null) + { + Loading organizations... + } + else + { + No organizations found. + } + + + + + + + + Page @_currentPage of @_totalPages + + + + + +@code { + private string _searchQuery = string.Empty; + private PagedResult _organizations; + private int _currentPage = 1; + private int _pageSize = 10; + private int _totalPages; + private int _totalItems; + private bool _isLoading = true; + + // Define pageSizeOptions array + private readonly int[] pageSizeOptions = { 6, 10, 15, 20 }; + + protected override async Task OnInitializedAsync() + { + try + { + _isLoading = true; + + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await SearchOrganizations(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.WriteLine($"Error during initialization: {ex.Message}"); + } + finally + { + _isLoading = false; + } + } + + private async Task SearchOrganizations() + { + try + { + _isLoading = true; + _organizations = await OrganizationsService.GetPaginatedOrganizationsAsync(_currentPage, _pageSize, _searchQuery); + _totalPages = _organizations?.TotalPages ?? 0; + _totalItems = _organizations?.TotalItems ?? 0; + _isLoading = false; + StateHasChanged(); + } + catch (Exception ex) + { + Console.WriteLine($"Error fetching organizations: {ex.Message}"); + _isLoading = false; + } + } + + private async Task OnPageChanged(int page) + { + _currentPage = page; + await SearchOrganizations(); + } + + private void NavigateToOrganization(Guid organizationId) + { + NavigationManager.NavigateTo($"/organizations/details/{organizationId}"); + } + + private async Task FollowOrganization(Guid organizationId) + { + try + { + await OrganizationsService.FollowOrganizationAsync(organizationId); + await SearchOrganizations(); // Refresh the list or update UI to reflect the follow status + } + catch (Exception ex) + { + Console.WriteLine($"Error following organization: {ex.Message}"); + } + } + + private bool IsUserMemberOfOrganization(OrganizationDto organization) + { + return organization.Users.Any(user => user.Id == IdentityService.UserDto.Id); + } + + private string GetOrganizationImage(string imageUrl) + { + return string.IsNullOrEmpty(imageUrl) + ? "/images/default_organization_profile_image.png" + : imageUrl; + } + + private string GetOrganizationBanner(string bannerUrl) + { + return string.IsNullOrEmpty(bannerUrl) + ? "/images/default_banner_image.png" + : bannerUrl; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OverviewComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OverviewComponent.razor new file mode 100644 index 000000000..c8a13dd48 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/OverviewComponent.razor @@ -0,0 +1,148 @@ +@page "/organizations/overview/{OrganizationId:guid}" +@inject IOrganizationsService OrganizationsService +@inject IEventsService EventsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject ISnackbar Snackbar +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO +@using MudBlazor + + + + @if (isLoading) + { + + } + else if (organization == null) + { + Failed to load organization overview. + } + else + { + + @organization.OrganizationDetails.Name + + + Description + @organization.OrganizationDetails.Description + + + + General Information + User Count: @organization.Users.Count() + Total Events: @events.Count() + + + + Members + + @foreach (var student in students) + { + + + + + @student.FirstName @student.LastName + + + + View Profile + + + + + } + + + + + Gallery + + @foreach (var image in organization.Gallery) + { + + + + + @image.DateAdded.ToShortDateString() + + + + } + + + + + Events + + @foreach (var eventItem in events) + { + + @eventItem.Name + + } + + + } + + + +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private OrganizationGalleryUsersDto organization; + private IEnumerable events; + private List students = new List(); + private bool isLoading = true; + + [Inject] private NavigationManager NavigationManager { get; set; } + + protected override async Task OnInitializedAsync() + { + isLoading = true; + try + { + await IdentityService.InitializeAuthenticationState(); + if (IdentityService.IsAuthenticated) + { + organization = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + var eventsResult = await EventsService.SearchEventsAsync(new SearchEvents { OrganizationId = OrganizationId }); + events = eventsResult.Items; + + foreach (var user in organization.Users) + { + var student = await StudentsService.GetStudentAsync(user.Id); + if (student != null) + { + students.Add(student); + } + } + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add("Failed to load organization overview.", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private string GetUserProfileImage(string profileImageUrl) + { + return string.IsNullOrEmpty(profileImageUrl) ? "/images/default_profile_image.png" : profileImageUrl; + } + + private void NavigateToProfile(Guid studentId) + { + NavigationManager.NavigateTo($"/user-details/{studentId}"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/RolesComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/RolesComponent.razor new file mode 100644 index 000000000..540a87a1a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/RolesComponent.razor @@ -0,0 +1,242 @@ +@page "/organizations/edit/{OrganizationId:guid}/roles" +@inject IOrganizationsService OrganizationsService +@inject IIdentityService IdentityService +@inject ISnackbar Snackbar +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO.Enums +@using MudBlazor + +@code { + [Parameter] public Guid OrganizationId { get; set; } + private OrganizationGalleryUsersDto organizationData; + private List organizationRoles = new(); + private RoleDto newRole = new RoleDto { Permissions = new Dictionary() }; + private RoleDto editingRole; + private bool isLoading = true; + private bool isEditing = false; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + + try + { + if (OrganizationId == Guid.Empty) + { + throw new ArgumentException("Invalid OrganizationId"); + } + + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + organizationData = await OrganizationsService.GetOrganizationWithGalleryAndUsersAsync(OrganizationId); + + if (organizationData?.OrganizationDetails != null) + { + organizationRoles = organizationData.OrganizationDetails.Roles.ToList(); + } + + foreach (Permission permission in Enum.GetValues(typeof(Permission))) + { + newRole.Permissions.Add(permission, false); + } + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (ArgumentException ex) + { + Snackbar.Add($"Error: {ex.Message}", Severity.Error); + } + catch (Exception ex) + { + Snackbar.Add("Failed to load organization roles.", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private void StartEditing(RoleDto role) + { + isEditing = true; + editingRole = new RoleDto + { + Id = role.Id, + Name = role.Name, + Description = role.Description, + Permissions = new Dictionary(role.Permissions) + }; + } + + private void CancelEditing() + { + isEditing = false; + editingRole = null; + } + + private async Task SaveRole() + { + try + { + if (OrganizationId == Guid.Empty) + { + throw new ArgumentException("Invalid OrganizationId"); + } + + if (isEditing) + { + var command = new UpdateRolePermissionsCommand( + organizationId: OrganizationId, + roleId: editingRole.Id, + roleName: editingRole.Name, + description: editingRole.Description, + permissions: editingRole.Permissions.ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value) + ); + + var response = await OrganizationsService.UpdateRolePermissionsAsync(OrganizationId, editingRole.Id, command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Role updated successfully.", Severity.Success); + var roleToUpdate = organizationRoles.FirstOrDefault(r => r.Id == editingRole.Id); + if (roleToUpdate != null) + { + roleToUpdate.Name = editingRole.Name; + roleToUpdate.Description = editingRole.Description; + roleToUpdate.Permissions = new Dictionary(editingRole.Permissions); + } + CancelEditing(); + } + else + { + Snackbar.Add("Failed to update role.", Severity.Error); + } + } + else + { + var command = new CreateOrganizationRoleCommand( + organizationId: OrganizationId, + roleName: newRole.Name, + description: newRole.Description, + permissions: newRole.Permissions.ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value) + ); + + var response = await OrganizationsService.CreateOrganizationRoleAsync(OrganizationId, command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Role created successfully.", Severity.Success); + organizationRoles.Add(newRole); + newRole = new RoleDto { Permissions = new Dictionary() }; + foreach (Permission permission in Enum.GetValues(typeof(Permission))) + { + newRole.Permissions.Add(permission, false); + } + } + else + { + Snackbar.Add("Failed to create role.", Severity.Error); + } + } + } + catch (ArgumentException ex) + { + Snackbar.Add($"Error: {ex.Message}", Severity.Error); + } + catch (Exception ex) + { + Snackbar.Add("An error occurred while saving the role.", Severity.Error); + Console.Error.WriteLine($"Exception: {ex}"); + } + } +} + +@if (isLoading) +{ + +} +else if (organizationData?.OrganizationDetails == null) +{ + Failed to load organization roles. +} +else +{ + + Roles + + + Role Name + Description + Permissions + Actions + + + @context.Name + @context.Description + + @foreach (var permission in context.Permissions) + { + @permission.Key.ToString() + } + + + + Edit + + + + + + + + @if (isEditing) + { + Edit Role + + + + Permissions + + @foreach (var permission in editingRole.Permissions.Keys.ToList()) + { + + + + } + + + + Save Role + + + Cancel + + } + else + { + Create New Role + + + + Permissions + + @foreach (var permission in newRole.Permissions.Keys.ToList()) + { + + + + } + + + + Create Role + + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/SubOrganizationsComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/SubOrganizationsComponent.razor new file mode 100644 index 000000000..bd90ccff0 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/SubOrganizationsComponent.razor @@ -0,0 +1,92 @@ +@page "/organizations/{OrganizationId:guid}/suborganizations" +@inject IOrganizationsService OrganizationsService +@inject ISnackbar Snackbar +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO.Organizations +@using MudBlazor + + + @if (isLoading) + { + + } + else if (subOrganizations == null || !subOrganizations.Any()) + { + No sub-organizations found. + } + else + { + + @foreach (var subOrg in subOrganizations) + { + + + + + @subOrg.Name + @subOrg.Description + + + + View Details + + + + + } + + } + + + + +@code { + [Parameter] + public Guid OrganizationId { get; set; } + + private List subOrganizations; + private bool isLoading = true; + private int currentPage = 1; + private int pageSize = 10; + private int totalItems; + + protected override async Task OnInitializedAsync() + { + await LoadSubOrganizations(); + } + + private async Task LoadSubOrganizations() + { + isLoading = true; + try + { + var result = await OrganizationsService.GetChildrenOrganizationsAsync(OrganizationId, currentPage, pageSize); + subOrganizations = result.Items.ToList(); + totalItems = result.TotalItems; + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load sub-organizations: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private void OnPageChanged(int page) + { + currentPage = page; + _ = LoadSubOrganizations(); + } + + private void NavigateToSubOrganization(Guid subOrganizationId) + { + NavigationManager.NavigateTo($"/organizations/details/{subOrganizationId}"); + } + + private string GetImageUrl(string imageUrl) + { + return string.IsNullOrWhiteSpace(imageUrl) ? "/images/default_organization_profile_image.png" : imageUrl; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/TreeViewOrganizations.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/TreeViewOrganizations.razor new file mode 100644 index 000000000..ae3b2255e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Organizations/TreeViewOrganizations.razor @@ -0,0 +1,181 @@ +@page "/organizations/tree" +@inject IOrganizationsService OrganizationsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@inject IIdentityService IdentityService +@using MudBlazor +@using Astravent.Web.Wasm.DTO.Organizations +@using System.Collections.Generic +@using System.Linq +@using System.Threading.Tasks + + + + + Organization Tree View + + View All Organizations + + + + + @if (_isLoading) + { + + } + else if (_userOrganizations != null && _userOrganizations.Any()) + { + @foreach (var org in _userOrganizations) + { + + + + + + @org.Name + @org.Description + + + + View + + + + + @if (org.SubOrganizations != null && org.SubOrganizations.Any()) + { + + @foreach (var subOrg in org.SubOrganizations) + { + + + + + + @subOrg.Name + @subOrg.Description + + + + View + + + + + + } + + } + + } + } + else + { + No organizations found. + } + + + + +@code { + private bool _isLoading = true; + private List _userOrganizations; + + protected override async Task OnInitializedAsync() + { + _isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + var userId = IdentityService.GetCurrentUserId(); + _userOrganizations = await LoadAllUserOrganizationsAsync(userId); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load organizations: {ex.Message}", Severity.Error); + } + finally + { + _isLoading = false; + } + } + + private async Task> LoadAllUserOrganizationsAsync(Guid userId) + { + var page = 1; + var pageSize = 100; + var allOrganizations = new List(); + + PagedResult pagedOrganizations; + do + { + pagedOrganizations = await OrganizationsService.GetPaginatedUserOrganizationsAsync(userId, page, pageSize); + allOrganizations.AddRange(pagedOrganizations.Items); + page++; + } while (pagedOrganizations.NextPage.HasValue); + + // Load sub-organizations recursively + foreach (var organization in allOrganizations) + { + await LoadSubOrganizationsAsync(organization); + } + + return allOrganizations; + } + + private async Task LoadSubOrganizationsAsync(OrganizationDto organization) + { + var subOrganizations = await OrganizationsService.GetAllChildrenOrganizationsAsync(organization.Id); + var subOrganizationDtos = new List(); + + foreach (var subOrgId in subOrganizations) + { + var subOrgDetails = await OrganizationsService.GetOrganizationAsync(subOrgId); + subOrganizationDtos.Add(subOrgDetails); + } + + organization.SubOrganizations = subOrganizationDtos; + + foreach (var subOrg in organization.SubOrganizations) + { + await LoadSubOrganizationsAsync(subOrg); + } + } + + private async Task OnExpandChanged(bool expanded, OrganizationDto organization) + { + organization.IsExpanded = expanded; + + if (expanded && !organization.SubOrganizations.Any()) + { + await LoadSubOrganizationsAsync(organization); + StateHasChanged(); + } + } + + private string GetOrganizationImage(string imageUrl) + { + return string.IsNullOrEmpty(imageUrl) + ? "/images/default_organization_profile_image.png" + : imageUrl; + } + + private void NavigateToOrganization(Guid organizationId) + { + NavigationManager.NavigateTo($"/organizations/details/{organizationId}"); + } + + private void NavigateToAllOrganizations() + { + NavigationManager.NavigateTo("/organizations/my"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/CommentItem.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/CommentItem.razor new file mode 100644 index 000000000..d6a65bc5e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/CommentItem.razor @@ -0,0 +1,193 @@ +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Comments +@inject ICommentsService CommentsService +@inject IStudentsService StudentsService + + + + + + + + @GetUserName(Comment.UserId) + @Comment.TextContent + @Comment.CreatedAt.ToString("g") + + + Reply + + + @if (isReplySectionVisible) + { + + Submit Reply + + @if (isLoadingReplies) + { + + } + else if (replies.Any()) + { + + @foreach (var reply in replies) + { + + } + + } + else + { + No replies available. + } + } + + + + +@code { + [Parameter] public CommentDto Comment { get; set; } + [Parameter] public PostDto Post { get; set; } + [Parameter] public Guid UserId { get; set; } + + private string newReplyText = string.Empty; + private bool isReplySectionVisible = false; + private bool isLoadingReplies = false; + private List replies = new(); // Use CommentDto instead of ReplyDto + private Dictionary studentsCache = new(); + + protected override async Task OnInitializedAsync() + { + // Ensure user details are loaded when the component is initialized + if (!studentsCache.ContainsKey(Comment.UserId)) + { + var student = await StudentsService.GetStudentAsync(Comment.UserId); + if (student != null) + { + studentsCache[Comment.UserId] = student; + } + } + } + + private string GetUserAvatar(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return studentsCache[userId.Value].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return $"{studentsCache[userId.Value].FirstName} {studentsCache[userId.Value].LastName}"; + } + return "Unknown User"; + } + + private void ToggleReplySection() + { + isReplySectionVisible = !isReplySectionVisible; + if (isReplySectionVisible) + { + LoadRepliesAsync(); + } + } + + private async Task SubmitReply() + { + if (string.IsNullOrWhiteSpace(newReplyText)) + { + return; + } + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: Post.Id, + commentContext: DetermineCommentContext(Post).ToString(), + userId: UserId, + parentId: Comment.Id, + textContent: newReplyText + ); + + var response = await CommentsService.CreateCommentAsync(command); + if (response.IsSuccessStatusCode) + { + newReplyText = string.Empty; + await LoadRepliesAsync(); + } + else + { + // Handle error + } + } + + private async Task LoadRepliesAsync() + { + isLoadingReplies = true; + + try + { + var command = new SearchSubCommentsCommand( + Post.Id, + DetermineCommentContext(Post).ToString(), + Comment.Id, + new PageableDto + { + Page = 1, + Size = 10, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "asc" + } + } + ); + + var response = await CommentsService.SearchSubCommentsAsync(command); + if (response != null && response.IsSuccessStatusCode) + { + replies = response.Content.Items.ToList(); // Store as CommentDto + + // Fetch user details for replies + foreach (var reply in replies) + { + if (!studentsCache.ContainsKey(reply.UserId)) + { + var student = await StudentsService.GetStudentAsync(reply.UserId); + if (student != null) + { + studentsCache[reply.UserId] = student; + } + } + } + } + else + { + replies = new List(); // Initialize replies to an empty list if response fails + } + } + catch (Exception ex) + { + // Handle any exceptions during loading of replies + Console.Error.WriteLine($"Failed to load replies: {ex.Message}"); + } + finally + { + isLoadingReplies = false; + } + } + + private CommentContext DetermineCommentContext(PostDto post) + { + if (post.OrganizationId.HasValue) + { + return post.EventId.HasValue ? CommentContext.OrganizationEvent : CommentContext.OrganizationPost; + } + else + { + return post.EventId.HasValue ? CommentContext.UserEvent : CommentContext.UserPost; + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/CommentSection.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/CommentSection.razor new file mode 100644 index 000000000..b8e4e4020 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/CommentSection.razor @@ -0,0 +1,112 @@ +@using Astravent.Web.Wasm.DTO +@inject ICommentsService CommentsService +@inject IStudentsService StudentsService + + + + Submit + + @if (comments.Any()) + { + + @foreach (var comment in comments.Where(c => c.ParentId == Guid.Empty)) + { + + } + + } + else + { + No comments available. + } + + +@code { + [Parameter] public PostDto Post { get; set; } + [Parameter] public Guid UserId { get; set; } + [Parameter] public EventCallback<(PostDto, string)> OnSubmitComment { get; set; } // Add the missing parameter + + private string newCommentText = string.Empty; + private List comments = new(); + private Dictionary studentsCache = new(); + + protected override async Task OnInitializedAsync() + { + await LoadCommentsForPostAsync(); + } + + private async Task LoadCommentsForPostAsync() + { + var command = new SearchRootCommentsCommand( + contextId: Post.Id, + commentContext: DetermineCommentContext(Post).ToString(), + pageable: new PageableDto + { + Page = 1, + Size = 10, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "asc" + } + } + ); + + var response = await CommentsService.SearchRootCommentsAsync(command); + comments = response.Items?.ToList() ?? new List(); + + foreach (var comment in comments) + { + if (!studentsCache.ContainsKey(comment.UserId)) + { + var student = await StudentsService.GetStudentAsync(comment.UserId); + if (student != null) + { + studentsCache[comment.UserId] = student; + } + } + + if (comment.Replies != null) + { + foreach (var reply in comment.Replies) + { + if (!studentsCache.ContainsKey(reply.UserId)) + { + var replyAuthor = await StudentsService.GetStudentAsync(reply.UserId); + if (replyAuthor != null) + { + studentsCache[reply.UserId] = replyAuthor; + } + } + } + } + } + } + + private CommentContext DetermineCommentContext(PostDto post) + { + if (post.OrganizationId.HasValue) + { + return post.EventId.HasValue ? CommentContext.OrganizationEvent : CommentContext.OrganizationPost; + } + else + { + return post.EventId.HasValue ? CommentContext.UserEvent : CommentContext.UserPost; + } + } + + private async Task SubmitComment() + { + if (string.IsNullOrWhiteSpace(newCommentText)) + { + // Notify user about empty comment + return; + } + + // Trigger the callback to notify the parent component + await OnSubmitComment.InvokeAsync((Post, newCommentText)); + + // Clear the text field after submitting the comment + newCommentText = string.Empty; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/PostItem.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/PostItem.razor new file mode 100644 index 000000000..88ad09864 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/PostItem.razor @@ -0,0 +1,152 @@ +@using Astravent.Web.Wasm.Pages.Reports.Dialogs +@inject NavigationManager NavigationManager + + + + + + + + + + @Username + @PostDate + + + + + + + @if (Post.MediaFiles != null && Post.MediaFiles.Count() == 1) + { + + } + else if (Post.MediaFiles != null && Post.MediaFiles.Any()) + { + + @foreach (var image in Post.MediaFiles) + { + +
+ +
+
+ } +
+ + } + @if (ReactionsSummaries.TryGetValue(Post.Id, out var reactionsSummary)) + { + + } +
+ + + + + + View + + + + + + Comment + + + + Report + + + + + + Share + + + + + Facebook + + + X (Twitter) + + + WhatsApp + + + LinkedIn + + + + + + @if (isCommentSectionVisible) + { + + } +
+ +@code { + [Parameter] public PostDto Post { get; set; } + [Parameter] public string Username { get; set; } + [Parameter] public string UserProfileImage { get; set; } + [Parameter] public string PostDate { get; set; } + [Parameter] public Dictionary ReactionsSummaries { get; set; } = new(); + [Parameter] public EventCallback OnViewPost { get; set; } + [Parameter] public EventCallback<(Guid, ReactionType)> OnReaction { get; set; } + [Parameter] public EventCallback<(PostDto, string)> OnSubmitComment { get; set; } + + [Inject] public IDialogService DialogService { get; set; } + [Inject] public IIdentityService IdentityService { get; set; } + + private bool isCommentSectionVisible; + + private void ToggleCommentSection() + { + isCommentSectionVisible = !isCommentSectionVisible; + } + + private void OpenReportDialog() + { + var parameters = new DialogParameters + { + { "Post", Post }, + { "Username", Username }, + { "UserProfileImage", UserProfileImage }, + { "IssuerId", IdentityService.GetCurrentUserIdFromJwtAsync() } + }; + + var options = new DialogOptions { CloseButton = true, MaxWidth = MaxWidth.Small }; + + DialogService.Show("Report Post", parameters, options); + } + + private void SharePost(string platform) + { + var postUrl = NavigationManager.Uri; + var postTitle = Uri.EscapeDataString(Post.TextContent); + + string shareUrl = platform switch + { + "facebook" => $"https://www.facebook.com/sharer/sharer.php?u={postUrl}", + "twitter" => $"https://twitter.com/intent/tweet?text={postTitle}&url={postUrl}", + "whatsapp" => $"https://wa.me/?text={postTitle}%20{postUrl}", + "linkedin" => $"https://www.linkedin.com/shareArticle?mini=true&url={postUrl}&title={postTitle}", + _ => "" + }; + + if (!string.IsNullOrEmpty(shareUrl)) + { + NavigationManager.NavigateTo(shareUrl, true); + } + } + + private void NavigateToUserProfile() + { + if (Post.UserId.HasValue) + { + NavigationManager.NavigateTo($"/user-details/{Post.UserId.Value}"); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/ReactionsMenu.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/ReactionsMenu.razor new file mode 100644 index 000000000..15552b167 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/ReactionsMenu.razor @@ -0,0 +1,27 @@ +@using Astravent.Web.Wasm.DTO + + + + + React + + + + @foreach (var reactionType in Enum.GetValues(typeof(ReactionType)).Cast()) + { + + @reactionType.GetReactionText() + + } + + + +@code { + [Parameter] public PostDto Post { get; set; } + [Parameter] public EventCallback<(Guid, ReactionType)> OnReact { get; set; } + + private void HandleReaction(ReactionType reactionType) + { + OnReact.InvokeAsync((Post.Id, reactionType)); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/ReactionsSummary.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/ReactionsSummary.razor new file mode 100644 index 000000000..ab746d7b6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/Components/ReactionsSummary.razor @@ -0,0 +1,22 @@ +@using Astravent.Web.Wasm.DTO + + + @foreach (var reaction in Summary.ReactionsWithCounts.OrderByDescending(r => r.Value)) + { + + + + @reaction.Value + + + } + + + Total: @Summary.NumberOfReactions + + + + +@code { + [Parameter] public ReactionsSummaryDto Summary { get; set; } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/CreatePost.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/CreatePost.razor new file mode 100644 index 000000000..21699c7f3 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/CreatePost.razor @@ -0,0 +1,211 @@ +@page "/posts/create" +@inject IPostsService PostsService +@inject IIdentityService IdentityService +@inject IMediaFilesService MediaFilesService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime +@using Astravent.Web.Wasm.Areas.Posts.CommandsDto +@using Astravent.Web.Wasm.DTO.Enums.Posts +@using MudBlazor + + + + + + + Post Preview + + + + + + + + Post Configuration + + + + Blog Post + Social Post + + + + @if (postType == PostType.BlogPost) + { + Blog Post Details + + + + + + + User Page + Organization Page + Event Page + + + + } + + + @if (postType == PostType.SocialPost) + { + Social Post Details + + + Upload Media Files + + + + + + + +@code { + private MudForm form; + private string textContent = "## Markdown Content\n"; // This is the dynamic content + private string title; + public List mediaFilesPreviews = new(); + private List uploadedMediaUrls = new List(); + private string state = "InDraft"; + private string visibility = "Visible"; + private DateTime? publishDate = DateTime.Now; + private bool isUploading = false; + private PostType postType = PostType.SocialPost; + private PostContext postContext = PostContext.UserPage; + private bool formIsValid = false; + + // Upload files method + private async Task UploadMediaFilesClick() + { + await JSRuntime.InvokeVoidAsync("eval", "document.getElementById('fileInputMediaFiles').click()"); + } + + private async Task UploadMediaFiles(InputFileChangeEventArgs e) + { + isUploading = true; + StateHasChanged(); + + const long maxAllowedSize = 10 * 1024 * 1024; + var files = e.GetMultipleFiles(); + + foreach (var file in files) + { + if (file.Size > maxAllowedSize) + { + Console.WriteLine($"File {file.Name} exceeds the allowed size limit."); + continue; + } + + using var stream = file.OpenReadStream(maxAllowedSize); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + var buffer = ms.ToArray(); + + var response = await MediaFilesService.UploadFileAsync( + sourceId: Guid.NewGuid(), + sourceType: "PostFileUser", + uploaderId: IdentityService.GetCurrentUserId(), + fileName: file.Name, + fileContentType: file.ContentType, + fileData: buffer); + + if (response.IsSuccessStatusCode) + { + var uploadedUrl = response.Content.FileUrl; + uploadedMediaUrls.Add(uploadedUrl); + + mediaFilesPreviews.Add(new FilePreview + { + Url = uploadedUrl, + ContentType = file.ContentType, + IsImage = file.ContentType.StartsWith("image/"), + IsVideo = file.ContentType.StartsWith("video/"), + IsPdf = file.ContentType.EndsWith("pdf") + }); + } + else + { + Console.WriteLine($"Failed to upload file {file.Name}: {response.ErrorMessage?.Reason}"); + } + } + + isUploading = false; + StateHasChanged(); + } + + private async Task HandleSubmit() + { + // Validate the form + await form.Validate(); + if (form.IsValid) + { + if ((postType == PostType.BlogPost && textContent.Length > 40000) || (postType == PostType.SocialPost && textContent.Length > 5000)) + { + Console.WriteLine("Post exceeds maximum character limit."); + return; + } + + var command = new CreatePostCommand + { + PostId = Guid.NewGuid(), + UserId = IdentityService.GetCurrentUserId(), + TextContent = textContent, + MediaFiles = uploadedMediaUrls.ToArray(), + State = state, + Visibility = visibility, + PublishDate = publishDate, + Context = postContext, + PostType = postType.ToString(), + Title = title + }; + + var response = await PostsService.CreatePostAsync(command); + + if (response.IsSuccessStatusCode) + { + NavigationManager.NavigateTo("/posts/my"); + } + else + { + Console.WriteLine("Failed to create post: " + response.ErrorMessage?.Reason); + } + } + else + { + formIsValid = false; + } + } + + private void HandleCancel() + { + NavigationManager.NavigateTo("/posts/my"); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/FilePreview.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/FilePreview.cs new file mode 100644 index 000000000..ff4e423fd --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/FilePreview.cs @@ -0,0 +1,8 @@ +public class FilePreview +{ + public string Url { get; set; } + public string ContentType { get; set; } + public bool IsImage { get; set; } + public bool IsVideo { get; set; } + public bool IsPdf { get; set; } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostDetails.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostDetails.razor new file mode 100644 index 000000000..22051a168 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostDetails.razor @@ -0,0 +1,592 @@ +@page "/posts/details/{postId:guid}" +@inject IPostsService PostsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject IReactionsService ReactionsService +@inject ICommentsService CommentsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using Microsoft.JSInterop +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Wrappers +@using MudBlazor +@using Astravent.Web.Wasm.Areas.Reactions.CommandDto +@using Astravent.Web.Wasm.DTO.Enums +@using System.Linq +@using System.Threading.Tasks +@using System.Collections.Generic + + + + @if (isLoading) + { + + } + else if (post == null) + { + Post not found. + } + else + { + + + + + + + + + @GetUserName(post.UserId) + @post.CreatedAt.ToString("g") + + + + + + + + @if (post.MediaFiles != null && post.MediaFiles.Any()) + { + @foreach (var mediaFile in post.MediaFiles) + { + + } + } + + + + + + + + @foreach (var reaction in reactionsSummary.ReactionsWithCounts.OrderByDescending(r => r.Value)) + { + + + @reaction.Value + + } + + + + + + + + + + + + + Facebook + + + Twitter + + + LinkedIn + + + WhatsApp + + + + + + + @foreach (var reactionType in Enum.GetValues(typeof(ReactionType)).Cast()) + { + + @reactionType.GetReactionText() + + } + + + Apply Reaction + + + + + + + + + + + + + + + + Submit + + + + Total Comments: @totalComments + + +
+ @if (comments.Any()) + { + + @foreach (var comment in comments) + { + + + + + + + @GetUserName(comment.UserId) + @comment.TextContent + @comment.CreatedAt.ToString("g") + + + @($"{comment.Likes?.Count() ?? 0} people liked this comment") + + + + + + + + + + + + + + @if (comment.Id == activeReplyCommentId) + { + + Submit Reply + } + + @if (comments.Any(c => c.ParentId == comment.Id)) + { + + @foreach (var reply in comments.Where(c => c.ParentId == comment.Id)) + { + + + + + + + @GetUserName(reply.UserId) + @reply.TextContent + @reply.CreatedAt.ToString("g") + + + @($"{reply.Likes?.Count() ?? 0} people liked this reply") + + + + + + + + + } + + } + + + + } + + + @if (!isLastPage) + { + + Load More Comments + + } + } + else + { + No comments available. + } +
+
+
+
+ } +
+
+ +@code { + [Parameter] public Guid postId { get; set; } + + private PostDto post; + private ReactionsSummaryDto reactionsSummary; + private List comments = new(); + private Dictionary studentsCache = new(); + private bool isLoading = true; + private Guid? activeReplyCommentId; + private string newCommentText = string.Empty; + private string newReplyText = string.Empty; + private int currentPage = 1; + private int pageSize = 10; + private int totalComments = 0; + private bool isLastPage = false; + private ElementReference commentSection; + private ReactionType selectedReaction = ReactionType.LikeIt; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await LoadPostDetailsAsync(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add($"Failed to load post details: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task LoadPostDetailsAsync() + { + try + { + post = await PostsService.GetPostAsync(postId); + + if (post == null) + { + Snackbar.Add("Post not found.", Severity.Warning); + return; + } + + if (post.UserId.HasValue) + { + var student = await StudentsService.GetStudentAsync(post.UserId.Value); + if (student != null) + { + studentsCache[post.UserId.Value] = student; + } + } + + reactionsSummary = await ReactionsService.GetReactionsSummaryAsync(postId, ReactionContentType.Post); + await LoadCommentsAsync(); + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load post details: {ex.Message}", Severity.Error); + } + } + + private void CacheStudentInfo(IEnumerable commentDtos) + { + foreach (var comment in commentDtos) + { + if (!studentsCache.ContainsKey(comment.UserId)) + { + var student = StudentsService.GetStudentAsync(comment.UserId).Result; + if (student != null) + { + studentsCache[comment.UserId] = student; + } + } + + if (comment.Replies != null) + { + foreach (var reply in comment.Replies) + { + if (!studentsCache.ContainsKey(reply.UserId)) + { + var replyAuthor = StudentsService.GetStudentAsync(reply.UserId).Result; + if (replyAuthor != null) + { + studentsCache[reply.UserId] = replyAuthor; + } + } + } + } + } + } + + private async Task LoadMoreCommentsAsync() + { + if (!isLastPage) + { + currentPage++; + await LoadCommentsAsync(); + } + } + + private async Task LoadCommentsAsync() + { + var command = new SearchRootCommentsCommand( + contextId: postId, + commentContext: DetermineCommentContext().ToString(), + pageable: new PageableDto + { + Page = currentPage, + Size = pageSize, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "asc" + } + } + ); + + var response = await CommentsService.SearchRootCommentsAsync(command); + + if (response != null) + { + // Update total comments and check if it is the last page + totalComments = response.TotalItems; + isLastPage = currentPage * pageSize >= totalComments; + + // Append new comments to the existing list + comments.AddRange(response.Items); + CacheStudentInfo(response.Items); + } + else + { + Snackbar.Add("Failed to load comments.", Severity.Error); + } + } + + private string GetUserAvatar(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return studentsCache[userId.Value].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return $"{studentsCache[userId.Value].FirstName} {studentsCache[userId.Value].LastName}"; + } + return "Unknown User"; + } + + private async Task HandleReactionAsync(ReactionType reactionType) + { + var existingReaction = await GetExistingReactionAsync(post.Id); + + string targetType = post.OrganizationId.HasValue ? "Organization" : "User"; + + if (existingReaction != null) + { + var updateReaction = new UpdateReactionDto + { + ReactionId = existingReaction.Id, + UserId = IdentityService.UserDto.Id, + NewReactionType = reactionType.ToString(), + ContentType = "Post", + TargetType = targetType + }; + + var updateResult = await ReactionsService.UpdateReactionAsync(updateReaction); + + if (updateResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction updated successfully!", Severity.Success); + reactionsSummary = await ReactionsService.GetReactionsSummaryAsync(postId, ReactionContentType.Post); + } + else + { + Snackbar.Add($"Failed to update reaction: {updateResult.ErrorMessage?.Reason}", Severity.Error); + } + } + else + { + var createReaction = new CreateReactionDto + { + UserId = IdentityService.UserDto.Id, + ContentId = post.Id, + ContentType = "Post", + ReactionType = reactionType.ToString(), + TargetType = targetType + }; + + var createResult = await ReactionsService.CreateReactionAsync(createReaction); + + if (createResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction added successfully!", Severity.Success); + reactionsSummary = await ReactionsService.GetReactionsSummaryAsync(postId, ReactionContentType.Post); + } + else + { + Snackbar.Add($"Failed to add reaction: {createResult.ErrorMessage?.Reason}", Severity.Error); + } + } + } + + private async Task GetExistingReactionAsync(Guid postId) + { + var reactions = await ReactionsService.GetReactionsAsync(postId, ReactionContentType.Post); + return reactions.FirstOrDefault(r => r.UserId == IdentityService.UserDto.Id); + } + + private void ToggleReplySection(Guid commentId) + { + if (activeReplyCommentId == commentId) + { + activeReplyCommentId = null; + newReplyText = string.Empty; + } + else + { + activeReplyCommentId = commentId; + newReplyText = string.Empty; + } + } + + private CommentContext DetermineCommentContext() + { + if (post.OrganizationId.HasValue) + { + return post.EventId.HasValue ? CommentContext.OrganizationEvent : CommentContext.OrganizationPost; + } + else + { + return post.EventId.HasValue ? CommentContext.UserEvent : CommentContext.UserPost; + } + } + + private async Task SubmitCommentAsync() + { + if (string.IsNullOrWhiteSpace(newCommentText)) + { + Snackbar.Add("Comment cannot be empty.", Severity.Warning); + return; + } + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: DetermineCommentContext().ToString(), + userId: IdentityService.GetCurrentUserId(), + parentId: Guid.Empty, + textContent: newCommentText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment added successfully!", Severity.Success); + newCommentText = string.Empty; + currentPage = 1; // Reset to the first page + comments.Clear(); // Clear the comments list + await LoadCommentsAsync(); + } + else + { + Snackbar.Add($"Failed to add comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task SubmitReplyAsync(CommentDto parentComment) + { + if (string.IsNullOrWhiteSpace(newReplyText)) + { + Snackbar.Add("Reply cannot be empty.", Severity.Warning); + return; + } + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: DetermineCommentContext().ToString(), + userId: IdentityService.GetCurrentUserId(), + parentId: parentComment.Id, + textContent: newReplyText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Reply added successfully!", Severity.Success); + newReplyText = string.Empty; + currentPage = 1; // Reset to the first page + comments.Clear(); // Clear the comments list + await LoadCommentsAsync(); + } + else + { + Snackbar.Add($"Failed to add reply: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task AddLikeToCommentAsync(CommentDto comment) + { + var command = new AddLikeDto(comment.Id, IdentityService.GetCurrentUserId(), DetermineCommentContext().ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment liked successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to like comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task AddLikeToReplyAsync(ReplyDto reply) + { + var command = new AddLikeDto(reply.Id, IdentityService.GetCurrentUserId(), DetermineCommentContext().ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Reply liked successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to like reply: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private void SharePost(string platform) + { + var postUrl = NavigationManager.Uri; + var encodedUrl = Uri.EscapeDataString(postUrl); + var shareUrl = platform switch + { + "facebook" => $"https://www.facebook.com/sharer/sharer.php?u={encodedUrl}", + "twitter" => $"https://twitter.com/intent/tweet?url={encodedUrl}", + "linkedin" => $"https://www.linkedin.com/shareArticle?mini=true&url={encodedUrl}", + "whatsapp" => $"https://api.whatsapp.com/send?text={encodedUrl}", + _ => "" + }; + + if (!string.IsNullOrEmpty(shareUrl)) + { + NavigationManager.NavigateTo(shareUrl, true); + } + else + { + Snackbar.Add("Failed to share the post.", Severity.Error); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostPreview.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostPreview.razor new file mode 100644 index 000000000..8ad732b0b --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostPreview.razor @@ -0,0 +1,86 @@ +@inject IStudentsService StudentsService +@inject IJSRuntime JSRuntime +@using MudBlazor + +@code { + [Parameter] public PostType PostType { get; set; } + [Parameter] public string Title { get; set; } + [Parameter] public string TextContent { get; set; } + [Parameter] public List MediaFiles { get; set; } = new(); + [Parameter] public Guid UserId { get; set; } + + private StudentDto student; + + protected override async Task OnInitializedAsync() + { + // Fetch the student data based on the user ID + student = await StudentsService.GetStudentAsync(UserId); + } + + // Helper methods to return student information (in case it's useful for multiple posts) + private string GetUserAvatar(Guid userId) => student?.ProfileImageUrl; + private string GetUserName(Guid userId) => $"{student?.FirstName} {student?.LastName}"; +} + + + + + + + + + + + @GetUserName(UserId) + + Posted at: @DateTime.Now.ToString("g") + + + + + @if (PostType == PostType.BlogPost) + { + Title: @Title + + } + else if (PostType == PostType.SocialPost) + { + + } + + + @if (MediaFiles.Any()) + { + + @foreach (var file in MediaFiles) + { + + + @if (file.IsImage) + { + + } + else if (file.IsVideo) + { + + + + } + else if (file.IsPdf) + { + + + View PDF + + + } + + + } + + } + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsList.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsList.razor new file mode 100644 index 000000000..400970559 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsList.razor @@ -0,0 +1,224 @@ +@page "/posts/search" +@inject IPostsService PostsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject IReactionsService ReactionsService +@inject ICommentsService CommentsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Wrappers +@using MudBlazor +@using Astravent.Web.Wasm.Pages.Posts.Components +@inject IDialogService DialogService + + + @if (isLoading) + { + + } + else if (!posts.Any()) + { + No posts available. + } + else + { + + @foreach (var post in posts) + { + + + + } + + + + + + + } + + +@code { + private List posts = new(); + private Dictionary studentsCache = new(); + private Dictionary reactionsSummaries = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalItems = 0; + private bool isLoading = true; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await LoadPostsAsync(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Snackbar.Add($"Failed to load posts: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task LoadPostsAsync() + { + var searchParams = new SearchPosts + { + Pageable = new PageableDto + { + Page = currentPage, + Size = pageSize, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "desc" + } + } + }; + + var response = await PostsService.SearchPostsAsync(searchParams); + + if (response.IsSuccessStatusCode) + { + var result = response.Content; + + posts = result?.Items?.ToList() ?? new List(); + totalItems = result.TotalItems; + + foreach (var post in posts) + { + if (post.UserId.HasValue && !studentsCache.ContainsKey(post.UserId.Value)) + { + var student = await StudentsService.GetStudentAsync(post.UserId.Value); + if (student != null) + { + studentsCache[post.UserId.Value] = student; + } + } + + // Get reactions summary for each post and store it in the dictionary + reactionsSummaries[post.Id] = await GetReactionsSummaryAsync(post.Id); + } + } + else + { + Snackbar.Add("No posts found.", Severity.Warning); + posts = new List(); + } + } + + private string GetUserAvatar(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return studentsCache[userId.Value].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return $"{studentsCache[userId.Value].FirstName} {studentsCache[userId.Value].LastName}"; + } + return "Unknown User"; + } + + private async Task OnPageChanged(int newPage) + { + currentPage = newPage; + await LoadPostsAsync(); + } + + private async Task GetReactionsSummaryAsync(Guid postId) + { + return await ReactionsService.GetReactionsSummaryAsync(postId, ReactionContentType.Post); + } + + private void NavigateToPostDetails(Guid postId) + { + NavigationManager.NavigateTo($"/posts/details/{postId}"); + } + + private async Task HandleReaction((Guid postId, ReactionType reactionType) reactionInfo) + { + var (postId, reactionType) = reactionInfo; + var existingReaction = await ReactionsService.GetReactionsAsync(postId, ReactionContentType.Post); + + var command = new UpdateReactionDto + { + ReactionId = existingReaction.FirstOrDefault()?.Id ?? Guid.NewGuid(), + UserId = IdentityService.GetCurrentUserId(), + NewReactionType = reactionType.ToString(), + ContentType = "Post", + TargetType = "User" + }; + + var result = await ReactionsService.UpdateReactionAsync(command); + if (result.IsSuccessStatusCode) + { + Snackbar.Add("Reaction updated successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to update reaction: {result.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task SubmitCommentAsync((PostDto post, string commentText) commentInfo) + { + var (post, commentText) = commentInfo; + if (string.IsNullOrWhiteSpace(commentText)) + { + Snackbar.Add("Comment cannot be empty.", Severity.Warning); + return; + } + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: "UserPost", + userId: IdentityService.GetCurrentUserId(), + parentId: Guid.Empty, + textContent: commentText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment added successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to add comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsListOLD.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsListOLD.razor new file mode 100644 index 000000000..23156e77f --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsListOLD.razor @@ -0,0 +1,592 @@ +@page "/posts/search/old" +@inject IPostsService PostsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject IReactionsService ReactionsService +@inject ICommentsService CommentsService +@inject NavigationManager NavigationManager +@inject ISnackbar Snackbar +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Wrappers +@using Astravent.Web.Wasm.Data.Posts +@using MudBlazor +@using Astravent.Web.Wasm.Areas.Reactions.CommandDto +@using Astravent.Web.Wasm.Areas.Comments.CommandsDto +@using System.Linq +@using System.Threading.Tasks +@using System.Collections.Generic + + + + @if (isLoading) + { + + } + else if (posts == null || !posts.Any()) + { + No posts available. + } + else + { + + + Latest Posts + + + @foreach (var post in posts) + { + + + + + + + + + @GetUserName(post.UserId) + @post.CreatedAt.ToString("g") + + + + + + @if (post.MediaFiles != null && post.MediaFiles.Any()) + { + + } + + @if (reactionsSummaries.TryGetValue(post.Id, out var reactionsSummary)) + { + + @foreach (var reaction in reactionsSummary.ReactionsWithCounts.OrderByDescending(r => r.Value)) + { + + + + @reaction.Value + + + } + + + Total: @reactionsSummary.NumberOfReactions + + + + } + + + + + + + View + + + + + + React + + + + @foreach (var reactionType in Enum.GetValues(typeof(ReactionType)).Cast()) + { + + @reactionType.GetReactionText() + + } + + + + + Comment + + + + @if (post.Id == activeCommentPostId) + { + + + Submit + + @if (postComments.TryGetValue(post.Id, out var comments) && comments.Any()) + { + + @foreach (var comment in comments.Where(c => c.ParentId == Guid.Empty)) + { + @* Render the main comment *@ + + + + + + + @GetUserName(comment.UserId) + @comment.TextContent + @comment.CreatedAt.ToString("g") + + + + @($"{comment.Likes?.Count() ?? 0} people liked this comment") + + + + + Reply + + + + + Like + + + @if (comment.Id == activeReplyCommentId) + { + + Submit Reply + } + + + @if (comments.Any(c => c.ParentId == comment.Id)) + { + + @foreach (var reply in comments.Where(c => c.ParentId == comment.Id)) + { + + + + + + + @GetUserName(reply.UserId) + @reply.TextContent + @reply.CreatedAt.ToString("g") + + + + @($"{reply.Likes?.Count() ?? 0} people liked this reply") + + + + + Like + + + + + } + + + } + + + + } + + } + else + { + No comments available. + } + + } + + + } + + + + + + + } + + + +@code { + private List posts = new(); + private Dictionary studentsCache = new(); + private Dictionary reactionsSummaries = new(); + private Dictionary> postComments = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalItems = 0; + private bool isLoading = true; + private Guid? activeCommentPostId; + private Guid? activeReplyCommentId; + private string newCommentText = string.Empty; + private string newReplyText = string.Empty; + + protected override async Task OnInitializedAsync() + { + isLoading = true; + + try + { + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + await LoadPostsAsync(); + } + else + { + NavigationManager.NavigateTo("/signin", forceLoad: true); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex); + Snackbar.Add($"Failed to load posts: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task LoadPostsAsync() + { + var searchParams = new SearchPosts + { + Pageable = new PageableDto + { + Page = currentPage, + Size = pageSize, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "desc" + } + } + }; + + var response = await PostsService.SearchPostsAsync(searchParams); + + if (response != null && response.IsSuccessStatusCode) + { + var result = response.Content; + + if (result != null && result.Items != null) + { + posts = result.Items.ToList(); + totalItems = result.TotalItems; + + foreach (var post in posts) + { + if (post.UserId.HasValue && !studentsCache.ContainsKey(post.UserId.Value)) + { + var student = await StudentsService.GetStudentAsync(post.UserId.Value); + if (student != null) + { + studentsCache[post.UserId.Value] = student; + } + } + + // Get reactions summary for each post and store it in the dictionary + reactionsSummaries[post.Id] = await GetReactionsSummaryAsync(post.Id); + + // Load comments for each post + postComments[post.Id] = await LoadCommentsForPostAsync(post); + } + } + else + { + Snackbar.Add("No posts found.", Severity.Warning); + posts = new List(); + } + } + else + { + Snackbar.Add($"Failed to load posts: {response?.ErrorMessage?.Reason}", Severity.Error); + } + } + + private string GetUserAvatar(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return studentsCache[userId.Value].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return $"{studentsCache[userId.Value].FirstName} {studentsCache[userId.Value].LastName}"; + } + return "Unknown User"; + } + + private async Task OnPageChanged(int newPage) + { + currentPage = newPage; + await LoadPostsAsync(); + } + + private void NavigateToPostDetails(Guid postId) + { + NavigationManager.NavigateTo($"/posts/details/{postId}"); + } + + private async Task GetReactionsSummaryAsync(Guid postId) + { + return await ReactionsService.GetReactionsSummaryAsync(postId, ReactionContentType.Post); + } + + private async Task HandleReactionAsync(PostDto post, ReactionType reactionType) + { + var existingReaction = await GetExistingReactionAsync(post.Id); + + string targetType = post.OrganizationId.HasValue ? "Organization" : "User"; + + if (existingReaction != null) + { + var updateReaction = new UpdateReactionDto + { + ReactionId = existingReaction.Id, + UserId = IdentityService.UserDto.Id, + NewReactionType = reactionType.ToString(), + ContentType = "Post", + TargetType = targetType + }; + + var updateResult = await ReactionsService.UpdateReactionAsync(updateReaction); + + if (updateResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction updated successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to update reaction: {updateResult.ErrorMessage?.Reason}", Severity.Error); + } + } + else + { + var createReaction = new CreateReactionDto + { + UserId = IdentityService.UserDto.Id, + ContentId = post.Id, + ContentType = "Post", + ReactionType = reactionType.ToString(), + TargetType = targetType + }; + + var createResult = await ReactionsService.CreateReactionAsync(createReaction); + + if (createResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction added successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to add reaction: {createResult.ErrorMessage?.Reason}", Severity.Error); + } + } + + await LoadPostsAsync(); + } + + private async Task GetExistingReactionAsync(Guid postId) + { + var reactions = await ReactionsService.GetReactionsAsync(postId, ReactionContentType.Post); + return reactions.FirstOrDefault(r => r.UserId == IdentityService.UserDto.Id); + } + + private void ToggleCommentSection(Guid postId) + { + if (activeCommentPostId == postId) + { + activeCommentPostId = null; + newCommentText = string.Empty; + } + else + { + activeCommentPostId = postId; + newCommentText = string.Empty; + } + } + + private void ToggleReplySection(Guid commentId) + { + if (activeReplyCommentId == commentId) + { + activeReplyCommentId = null; + newReplyText = string.Empty; + } + else + { + activeReplyCommentId = commentId; + newReplyText = string.Empty; + } + } + + private async Task> LoadCommentsForPostAsync(PostDto post) + { + var command = new SearchRootCommentsCommand( + contextId: post.Id, + commentContext: DetermineCommentContext(post).ToString(), + pageable: new PageableDto + { + Page = 1, + Size = 10, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "asc" + } + } + ); + + var response = await CommentsService.SearchRootCommentsAsync(command); + var comments = response.Items?.ToList() ?? new List(); + + // Fetch user details for each comment author and reply author + foreach (var comment in comments) + { + if (!studentsCache.ContainsKey(comment.UserId)) + { + var student = await StudentsService.GetStudentAsync(comment.UserId); + if (student != null) + { + studentsCache[comment.UserId] = student; + } + } + + if (comment.Replies != null) + { + foreach (var reply in comment.Replies) + { + if (!studentsCache.ContainsKey(reply.UserId)) + { + var replyAuthor = await StudentsService.GetStudentAsync(reply.UserId); + if (replyAuthor != null) + { + studentsCache[reply.UserId] = replyAuthor; + } + } + } + } + } + + return comments; + } + + private CommentContext DetermineCommentContext(PostDto post) + { + if (post.OrganizationId.HasValue) + { + return post.EventId.HasValue ? CommentContext.OrganizationEvent : CommentContext.OrganizationPost; + } + else + { + return post.EventId.HasValue ? CommentContext.UserEvent : CommentContext.UserPost; + } + } + + private async Task SubmitCommentAsync(PostDto post) + { + if (string.IsNullOrWhiteSpace(newCommentText)) + { + Snackbar.Add("Comment cannot be empty.", Severity.Warning); + return; + } + + var commentContext = DetermineCommentContext(post); + var userId = IdentityService.GetCurrentUserId(); + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: commentContext.ToString(), + userId: userId, + parentId: Guid.Empty, + textContent: newCommentText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment added successfully!", Severity.Success); + newCommentText = string.Empty; + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to add comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task SubmitReplyAsync(PostDto post, CommentDto parentComment) + { + if (string.IsNullOrWhiteSpace(newReplyText)) + { + Snackbar.Add("Reply cannot be empty.", Severity.Warning); + return; + } + + var commentContext = DetermineCommentContext(post); + var userId = IdentityService.GetCurrentUserId(); + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: commentContext.ToString(), + userId: userId, + parentId: parentComment.Id, + textContent: newReplyText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Reply added successfully!", Severity.Success); + newReplyText = string.Empty; + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to add reply: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task AddLikeToCommentAsync(CommentDto comment, PostDto post) + { + var command = new AddLikeDto(comment.Id, IdentityService.UserDto.Id, DetermineCommentContext(post).ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment liked successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to like comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task AddLikeToReplyAsync(ReplyDto reply, PostDto post) + { + var command = new AddLikeDto(reply.Id, IdentityService.UserDto.Id, DetermineCommentContext(post).ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Reply liked successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to like reply: {response.ErrorMessage?.Reason}", Severity.Error); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsMy.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsMy.razor new file mode 100644 index 000000000..340632c73 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsMy.razor @@ -0,0 +1,251 @@ +@page "/posts/my" +@inject IPostsService PostsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject IReactionsService ReactionsService +@inject ICommentsService CommentsService +@inject ISnackbar Snackbar +@inject NavigationManager NavigationManager +@inject IDialogService DialogService +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Wrappers +@using MudBlazor +@using Astravent.Web.Wasm.Pages.Posts.Components +@using System.Linq +@using System.Collections.Generic +@using System.Threading.Tasks + + + @if (isLoading) + { + + } + else if (!posts.Any()) + { + You haven't created any posts yet. + } + else + { + + @foreach (var post in posts) + { + + + + } + + + + + + + } + + +@code { + private List posts = new(); + private Dictionary studentsCache = new(); + private Dictionary reactionsSummaries = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalItems = 0; + private bool isLoading = true; + private Guid? currentUserId; + + protected override async Task OnInitializedAsync() + { + try + { + isLoading = true; + + // Initialize the authentication and get the current user ID + await IdentityService.InitializeAuthenticationState(); + + if (IdentityService.IsAuthenticated) + { + currentUserId = IdentityService.GetCurrentUserId(); + + if (currentUserId.HasValue) + { + await LoadPostsAsync(); + } + else + { + throw new InvalidOperationException("Failed to retrieve the current user ID."); + } + } + else + { + Snackbar.Add("You need to be logged in to view your posts.", Severity.Warning); + NavigationManager.NavigateTo("/login"); + } + } + catch (Exception ex) + { + Snackbar.Add($"Error initializing page: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task LoadPostsAsync() + { + if (!currentUserId.HasValue) + return; + + var searchParams = new SearchPosts + { + Pageable = new PageableDto + { + Page = currentPage, + Size = pageSize, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "desc" + } + }, + UserId = currentUserId.Value + }; + + try + { + var response = await PostsService.SearchPostsAsync(searchParams); + + if (response.IsSuccessStatusCode) + { + var result = response.Content; + + if (result != null && result.Items != null) + { + posts = result.Items.ToList(); + totalItems = result.TotalItems; + + foreach (var post in posts.Where(p => p.UserId.HasValue)) + { + if (!studentsCache.ContainsKey(post.UserId.Value)) + { + var student = await StudentsService.GetStudentAsync(post.UserId.Value); + if (student != null) + { + studentsCache[post.UserId.Value] = student; + } + } + + reactionsSummaries[post.Id] = await ReactionsService.GetReactionsSummaryAsync(post.Id, ReactionContentType.Post); + } + } + else + { + Snackbar.Add("No posts found.", Severity.Warning); + posts = new List(); + } + } + else + { + Snackbar.Add($"Failed to load posts: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Error loading posts: {ex.Message}", Severity.Error); + } + } + + private string GetUserAvatar(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return studentsCache[userId.Value].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid? userId) + { + if (userId.HasValue && studentsCache.ContainsKey(userId.Value)) + { + return $"{studentsCache[userId.Value].FirstName} {studentsCache[userId.Value].LastName}"; + } + return "Unknown User"; + } + + private async Task OnPageChanged(int newPage) + { + currentPage = newPage; + await LoadPostsAsync(); + } + + private void NavigateToPostDetails(Guid postId) + { + NavigationManager.NavigateTo($"/posts/details/{postId}"); + } + + private async Task HandleReaction((Guid postId, ReactionType reactionType) reactionInfo) + { + var (postId, reactionType) = reactionInfo; + var existingReaction = await ReactionsService.GetReactionsAsync(postId, ReactionContentType.Post); + + var command = new UpdateReactionDto + { + ReactionId = existingReaction.FirstOrDefault()?.Id ?? Guid.NewGuid(), + UserId = currentUserId.Value, + NewReactionType = reactionType.ToString(), + ContentType = "Post", + TargetType = "User" + }; + + var result = await ReactionsService.UpdateReactionAsync(command); + if (result.IsSuccessStatusCode) + { + Snackbar.Add("Reaction updated successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to update reaction: {result.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task SubmitCommentAsync((PostDto post, string commentText) commentInfo) + { + var (post, commentText) = commentInfo; + if (string.IsNullOrWhiteSpace(commentText)) + { + Snackbar.Add("Comment cannot be empty.", Severity.Warning); + return; + } + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: "UserPost", + userId: currentUserId.Value, + parentId: Guid.Empty, + textContent: commentText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment added successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to add comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsMyOLD.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsMyOLD.razor new file mode 100644 index 000000000..2125abdd9 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Posts/PostsMyOLD.razor @@ -0,0 +1,602 @@ +@page "/posts/my/old" +@inject IPostsService PostsService +@inject IStudentsService StudentsService +@inject IIdentityService IdentityService +@inject IReactionsService ReactionsService +@inject ICommentsService CommentsService +@inject ISnackbar Snackbar +@inject NavigationManager NavigationManager +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Wrappers +@using MudBlazor +@using System.Linq +@using System.Threading.Tasks +@using System.Collections.Generic +@using Astravent.Web.Wasm.Areas.Reactions +@using Astravent.Web.Wasm.Areas.Reactions.CommandDto +@using Astravent.Web.Wasm.Areas.Comments.CommandsDto + + + + @if (isLoading) + { + + } + else if (posts == null || !posts.Any()) + { + You have no posts yet. + } + else + { + + + My Posts + + + @foreach (var post in posts) + { + + + + + + + + + @GetUserName(post.UserId.Value) + @post.CreatedAt.ToString("g") + + + + + + @if (post.MediaFiles != null && post.MediaFiles.Any()) + { + + } + + @if (reactionsSummaries.TryGetValue(post.Id, out var reactionsSummary)) + { + + @foreach (var reaction in reactionsSummary.ReactionsWithCounts.OrderByDescending(r => r.Value)) + { + + + + @reaction.Value + + + } + + + Total: @reactionsSummary.NumberOfReactions + + + + } + + + + + + + View + + + + + + React + + + + @foreach (var reactionType in Enum.GetValues(typeof(ReactionType)).Cast()) + { + + @reactionType.GetReactionText() + + } + + + + + Comment + + + + @if (post.Id == activeCommentPostId) + { + + + Submit + + @if (postComments.TryGetValue(post.Id, out var comments) && comments.Any()) + { + + @foreach (var comment in comments.Where(c => c.ParentId == Guid.Empty)) + { + + + + + + + @GetUserName(comment.UserId) + @comment.TextContent + @comment.CreatedAt.ToString("g") + + + + @($"{comment.Likes?.Count() ?? 0} people liked this comment") + + + + + Reply + + + + + Like + + + @if (comment.Id == activeReplyCommentId) + { + + Submit Reply + } + + + @if (comments.Any(c => c.ParentId == comment.Id)) + { + + @foreach (var reply in comments.Where(c => c.ParentId == comment.Id)) + { + + + + + + + @GetUserName(reply.UserId) + @reply.TextContent + @reply.CreatedAt.ToString("g") + + + + @($"{reply.Likes?.Count() ?? 0} people liked this reply") + + + + + Like + + + + + } + + } + + + + } + + } + else + { + No comments available. + } + + } + + + } + + + + + + + + } + + + +@code { + private List posts = new(); + private Dictionary studentsCache = new(); + private Dictionary reactionsSummaries = new(); + private Dictionary> postComments = new(); + private int currentPage = 1; + private int pageSize = 10; + private int totalItems = 0; + private bool isLoading = true; + private Guid? activeCommentPostId; + private Guid? activeReplyCommentId; + private string newCommentText = string.Empty; + private string newReplyText = string.Empty; + private Guid? currentUserId; + + protected override async Task OnInitializedAsync() + { + try + { + isLoading = true; + + if (IdentityService.IsAuthenticated) + { + currentUserId = IdentityService.GetCurrentUserId(); + + if (currentUserId.HasValue) + { + await LoadPostsAsync(); + } + else + { + throw new InvalidOperationException("Failed to retrieve the current user ID."); + } + } + else + { + Snackbar.Add("You need to be logged in to view your posts.", Severity.Warning); + NavigationManager.NavigateTo("/login"); + } + } + catch (Exception ex) + { + Snackbar.Add($"Error initializing page: {ex.Message}", Severity.Error); + } + finally + { + isLoading = false; + } + } + + private async Task LoadPostsAsync() + { + if (!currentUserId.HasValue) + return; + + var searchParams = new SearchPosts + { + Pageable = new PageableDto + { + Page = currentPage, + Size = pageSize, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "desc" + } + }, + UserId = currentUserId.Value + }; + + try + { + var response = await PostsService.SearchPostsAsync(searchParams); + + if (response.IsSuccessStatusCode) + { + var result = response.Content; + + if (result != null && result.Items != null) + { + posts = result.Items.ToList(); + totalItems = result.TotalItems; + + foreach (var post in posts.Where(p => p.UserId.HasValue)) + { + if (!studentsCache.ContainsKey(post.UserId.Value)) + { + var student = await StudentsService.GetStudentAsync(post.UserId.Value); + if (student != null) + { + studentsCache[post.UserId.Value] = student; + } + } + + reactionsSummaries[post.Id] = await ReactionsService.GetReactionsSummaryAsync(post.Id, ReactionContentType.Post); + + // Load comments for each post + postComments[post.Id] = await LoadCommentsForPostAsync(post); + } + } + else + { + Snackbar.Add("No posts found.", Severity.Warning); + posts = new List(); + } + } + else + { + Snackbar.Add($"Failed to load posts: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Error loading posts: {ex.Message}", Severity.Error); + } + } + + private string GetUserAvatar(Guid userId) + { + if (studentsCache.ContainsKey(userId)) + { + return studentsCache[userId].ProfileImageUrl ?? string.Empty; + } + return string.Empty; + } + + private string GetUserName(Guid userId) + { + if (studentsCache.ContainsKey(userId)) + { + return $"{studentsCache[userId].FirstName} {studentsCache[userId].LastName}"; + } + return "Unknown User"; + } + + private async Task OnPageChanged(int newPage) + { + currentPage = newPage; + await LoadPostsAsync(); + } + + private void NavigateToPostDetails(Guid postId) + { + NavigationManager.NavigateTo($"/posts/details/{postId}"); + } + + private async Task HandleReactionAsync(PostDto post, ReactionType reactionType) + { + var existingReaction = await GetExistingReactionAsync(post.Id); + + if (existingReaction != null) + { + var updateReaction = new UpdateReactionDto + { + ReactionId = existingReaction.Id, + UserId = currentUserId.Value, + NewReactionType = reactionType.ToString(), + ContentType = "Post", + TargetType = post.OrganizationId.HasValue ? "Organization" : "User" + }; + + var updateResult = await ReactionsService.UpdateReactionAsync(updateReaction); + + if (updateResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction updated successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to update reaction: {updateResult.ErrorMessage?.Reason}", Severity.Error); + } + } + else + { + var createReaction = new CreateReactionDto + { + UserId = currentUserId.Value, + ContentId = post.Id, + ContentType = "Post", + ReactionType = reactionType.ToString(), + TargetType = post.OrganizationId.HasValue ? "Organization" : "User" + }; + + var createResult = await ReactionsService.CreateReactionAsync(createReaction); + + if (createResult.IsSuccessStatusCode) + { + Snackbar.Add("Reaction added successfully!", Severity.Success); + } + else + { + Snackbar.Add($"Failed to add reaction: {createResult.ErrorMessage?.Reason}", Severity.Error); + } + } + + await LoadPostsAsync(); + } + + private async Task GetExistingReactionAsync(Guid postId) + { + var reactions = await ReactionsService.GetReactionsAsync(postId, ReactionContentType.Post); + return reactions.FirstOrDefault(r => r.UserId == currentUserId.Value); + } + + private void ToggleCommentSection(Guid postId) + { + if (activeCommentPostId == postId) + { + activeCommentPostId = null; + newCommentText = string.Empty; + } + else + { + activeCommentPostId = postId; + newCommentText = string.Empty; + } + } + + private void ToggleReplySection(Guid commentId) + { + if (activeReplyCommentId == commentId) + { + activeReplyCommentId = null; + newReplyText = string.Empty; + } + else + { + activeReplyCommentId = commentId; + newReplyText = string.Empty; + } + } + + private async Task> LoadCommentsForPostAsync(PostDto post) + { + var command = new SearchRootCommentsCommand( + contextId: post.Id, + commentContext: DetermineCommentContext(post).ToString(), + pageable: new PageableDto + { + Page = 1, + Size = 10, + Sort = new SortDto + { + SortBy = new[] { "CreatedAt" }, + Direction = "asc" + } + } + ); + + var response = await CommentsService.SearchRootCommentsAsync(command); + var comments = response.Items?.ToList() ?? new List(); + + // Fetch user details for each comment author and reply author + foreach (var comment in comments) + { + if (!studentsCache.ContainsKey(comment.UserId)) + { + var student = await StudentsService.GetStudentAsync(comment.UserId); + if (student != null) + { + studentsCache[comment.UserId] = student; + } + } + + if (comment.Replies != null) + { + foreach (var reply in comment.Replies) + { + if (!studentsCache.ContainsKey(reply.UserId)) + { + var replyAuthor = await StudentsService.GetStudentAsync(reply.UserId); + if (replyAuthor != null) + { + studentsCache[reply.UserId] = replyAuthor; + } + } + } + } + } + + return comments; + } + + private CommentContext DetermineCommentContext(PostDto post) + { + if (post.OrganizationId.HasValue) + { + return post.EventId.HasValue ? CommentContext.OrganizationEvent : CommentContext.OrganizationPost; + } + else + { + return post.EventId.HasValue ? CommentContext.UserEvent : CommentContext.UserPost; + } + } + + private async Task SubmitCommentAsync(PostDto post) + { + if (string.IsNullOrWhiteSpace(newCommentText)) + { + Snackbar.Add("Comment cannot be empty.", Severity.Warning); + return; + } + + var commentContext = DetermineCommentContext(post); + var userId = currentUserId.Value; + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: commentContext.ToString(), + userId: userId, + parentId: Guid.Empty, + textContent: newCommentText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment added successfully!", Severity.Success); + newCommentText = string.Empty; + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to add comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task SubmitReplyAsync(PostDto post, CommentDto parentComment) + { + if (string.IsNullOrWhiteSpace(newReplyText)) + { + Snackbar.Add("Reply cannot be empty.", Severity.Warning); + return; + } + + var commentContext = DetermineCommentContext(post); + var userId = currentUserId.Value; + + var command = new CreateCommentCommand( + commentId: Guid.NewGuid(), + contextId: post.Id, + commentContext: commentContext.ToString(), + userId: userId, + parentId: parentComment.Id, + textContent: newReplyText + ); + + var response = await CommentsService.CreateCommentAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Reply added successfully!", Severity.Success); + newReplyText = string.Empty; + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to add reply: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task AddLikeToCommentAsync(CommentDto comment, PostDto post) + { + var command = new AddLikeDto(comment.Id, currentUserId.Value, DetermineCommentContext(post).ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Comment liked successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to like comment: {response.ErrorMessage?.Reason}", Severity.Error); + } + } + + private async Task AddLikeToReplyAsync(ReplyDto reply, PostDto post) + { + var command = new AddLikeDto(reply.Id, currentUserId.Value, DetermineCommentContext(post).ToString()); + + var response = await CommentsService.AddLikeAsync(command); + + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Reply liked successfully!", Severity.Success); + await LoadPostsAsync(); + } + else + { + Snackbar.Add($"Failed to like reply: {response.ErrorMessage?.Reason}", Severity.Error); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Reports/Dialogs/PostReportDialog.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Reports/Dialogs/PostReportDialog.razor new file mode 100644 index 000000000..7475d73d5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Pages/Reports/Dialogs/PostReportDialog.razor @@ -0,0 +1,106 @@ + + Report Post + + + + @if (Post != null && !string.IsNullOrEmpty(Username)) + { + + + + + + + + @Username + @Post.CreatedAt.ToString("g") + + + + @if (Post.MediaFiles != null && Post.MediaFiles.Any()) + { + + } + + + + + + @foreach (ReportCategory category in Enum.GetValues(typeof(ReportCategory))) + { + @ReportCategoryExtensions.GetReportCategoryText(category) + } + + + + You are reporting the post by @Username. Please provide the reason for your report below: + + } + else + { + Unable to load post details. Please try again. + } + + + + Submit Report + Cancel + + + +@code { + [CascadingParameter] MudDialogInstance MudDialog { get; set; } + [Parameter] public PostDto Post { get; set; } + [Parameter] public string Username { get; set; } + [Parameter] public string UserProfileImage { get; set; } + [Parameter] public Guid IssuerId { get; set; } + + [Inject] public IReportsService ReportsService { get; set; } + [Inject] public ISnackbar Snackbar { get; set; } + + private ReportCategory selectedCategory = ReportCategory.Spam; // Default selection + private string reason = string.Empty; + + private async Task SubmitReport() + { + if (string.IsNullOrWhiteSpace(reason)) + { + Snackbar.Add("Please provide a reason for your report.", Severity.Warning); + return; + } + + try + { + var reportCommand = new CreateReportCommand( + reportId: Guid.NewGuid(), + issuerId: IssuerId, // Use IssuerId parameter + targetId: Post.Id, + targetOwnerId: Post.UserId.GetValueOrDefault(), + contextType: "Post", + category: ReportCategoryExtensions.GetReportCategoryText(selectedCategory), // Use selected category + reason: reason + ); + + var response = await ReportsService.CreateReportAsync(reportCommand); + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Report submitted successfully.", Severity.Success); + MudDialog.Close(); + } + else + { + Snackbar.Add("Failed to submit the report.", Severity.Error); + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.Message); + Snackbar.Add("An error occurred while submitting the report.", Severity.Error); + } + } + + private void Cancel() + { + MudDialog.Cancel(); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Program.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Program.cs new file mode 100644 index 000000000..fa7b4b7d4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Program.cs @@ -0,0 +1,84 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Astravent.Web.Wasm; +using Blazored.LocalStorage; +using MudBlazor.Services; +using Radzen; +using Microsoft.AspNetCore.Components.Authorization; +using System.Net.Http; +using System; +using Astravent.Web.Wasm.Areas.Http; +using Astravent.Web.Wasm.Models.Identity; +using Astravent.Web.Wasm.Areas.Events; +using Astravent.Web.Wasm.Areas.Friends; +using Astravent.Web.Wasm.Areas.Communication; +using Astravent.Web.Wasm.Areas.Posts; +using Astravent.Web.Wasm.Areas.Students; +using Astravent.Web.Wasm.Areas.Organizations; +using Astravent.Web.Wasm.Areas.Notifications; +using Astravent.Web.Wasm.Areas.MediaFiles; +using Astravent.Web.Wasm.Areas.Reactions; +using Astravent.Web.Wasm.Areas.Comments; +using Astravent.Web.Wasm.Areas.Reports; +using MudBlazor; +using Astravent.Web.Wasm.Areas.Identity; +using Astravent.Web.Wasm.HttpClients; +using Astravent.Web.Wasm.Utilities; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +if (builder.HostEnvironment.IsDevelopment()) +{ + builder.Configuration.AddJsonFile("appsettings.Development.json", optional: false, reloadOnChange: true); +} +else +{ + builder.Configuration.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); +} + +var httpClientOptions = builder.Configuration.GetSection("HttpClientOptions").Get(); + +if (httpClientOptions == null || string.IsNullOrEmpty(httpClientOptions.ApiUrl)) +{ + throw new InvalidOperationException("HttpClientOptions must be configured with a valid ApiUrl."); +} + +builder.Services.AddSingleton(httpClientOptions); + +builder.Services.AddHttpClient((serviceProvider, client) => +{ + var options = serviceProvider.GetRequiredService(); + client.BaseAddress = new Uri(options.ApiUrl); +}); + + +builder.Services.AddMudServices(); +builder.Services.AddMudMarkdownServices(); +builder.Services.AddBlazoredLocalStorage(); + +builder.Services.AddScoped(); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddAuthorizationCore(); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +await builder.Build().RunAsync(); diff --git a/MiniSpace.Web/src/MiniSpacePwa/Properties/launchSettings.json b/MiniSpace.Web/src/Astravent.Web.Wasm/Properties/launchSettings.json similarity index 92% rename from MiniSpace.Web/src/MiniSpacePwa/Properties/launchSettings.json rename to MiniSpace.Web/src/Astravent.Web.Wasm/Properties/launchSettings.json index e4724733d..0ae8558ae 100644 --- a/MiniSpace.Web/src/MiniSpacePwa/Properties/launchSettings.json +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Properties/launchSettings.json @@ -14,7 +14,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "http://localhost:5228", + "applicationUrl": "http://localhost:5606", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -24,7 +24,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:7170;http://localhost:5228", + "applicationUrl": "https://localhost:7170;http://localhost:5606", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/AuthWrapper.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/AuthWrapper.razor new file mode 100644 index 000000000..63df10ef4 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/AuthWrapper.razor @@ -0,0 +1,39 @@ +@page "/auth-wrapper" +@using Microsoft.AspNetCore.Components.Authorization +@inject AuthenticationStateProvider AuthenticationStateProvider +@inject NavigationManager NavigationManager + +@if (isLoading) +{ +
Loading...
+} +else if (!isAuthenticated) +{ +
Redirecting to sign-in...
+} +else +{ + @ChildContent +} + +@code { + [Parameter] public RenderFragment ChildContent { get; set; } + private bool isLoading = true; + private bool isAuthenticated = false; + + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + isAuthenticated = authState.User.Identity.IsAuthenticated; + + if (!isAuthenticated) + { + NavigationManager.NavigateTo("/signin", true); + } + else + { + isLoading = false; + StateHasChanged(); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/EmojiList.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/EmojiList.cs new file mode 100644 index 000000000..24d12571c --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/EmojiList.cs @@ -0,0 +1,60 @@ +// EmojiList.cs +using System.Collections.Generic; + +public static class EmojiList +{ + public static List Emojis { get; } = new List + { + // Smileys & Emotion + "😀", "😁", "😂", "🤣", "😃", "😄", "😅", "😆", "😉", "😊", "😋", "😎", "😍", "😘", "🥰", "😗", + "😙", "😚", "🙂", "🤗", "🤩", "🤔", "🤨", "😐", "😑", "😶", "🙄", "😏", "😣", "😥", "😮", "🤐", + "😯", "😪", "😫", "🥱", "😴", "😛", "😜", "🤪", "😝", "🤑", "🤭", "🤫", "🤥", "😒", "😓", "😔", + "😕", "🙃", "🤑", "😲", "😷", "🤒", "🤕", "🤢", "🤮", "🤧", "🥵", "🥶", "🥳", "🤯", "😬", "😡", + "😠", "😤", "😭", "😱", "😨", "😰", "😥", "😢", "😓", "🤧", "🥺", "😞", "😔", "😟", "😕", "🙁", + "😣", "😖", "😫", "😩", "🥱", "😤", "😡", "😠", "🤬", "😈", "👿", "💀", "☠️", "💩", "🤡", "👹", "👺", + "👻", "👽", "👾", "🤖", "🎃", "😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾", + + // People & Body + "👶", "👧", "🧒", "👦", "👩", "🧑", "👨", "👩‍🦱", "👨‍🦱", "👩‍🦳", "👨‍🦳", "👩‍🦲", "👨‍🦲", "👩‍🦰", "👨‍🦰", + "👵", "🧓", "👴", "👲", "👳‍♀️", "👳‍♂️", "🧕", "👮‍♀️", "👮‍♂️", "👷‍♀️", "👷‍♂️", "💂‍♀️", "💂‍♂️", "🕵️‍♀️", + "🕵️‍♂️", "👩‍⚕️", "👨‍⚕️", "👩‍🌾", "👨‍🌾", "👩‍🍳", "👨‍🍳", "👩‍🎓", "👨‍🎓", "👩‍🎤", "👨‍🎤", "👩‍🏫", "👨‍🏫", + "👩‍🏭", "👨‍🏭", "👩‍💻", "👨‍💻", "👩‍💼", "👨‍💼", "👩‍🔧", "👨‍🔧", "👩‍🔬", "👨‍🔬", "👩‍🎨", "👨‍🎨", "👩‍🚒", + "👨‍🚒", "👩‍✈️", "👨‍✈️", "👩‍🚀", "👨‍🚀", "👩‍⚖️", "👨‍⚖️", "👰‍♀️", "🤵‍♂️", "👸", "🤴", "🦸‍♀️", "🦸‍♂️", + "🦹‍♀️", "🦹‍♂️", "🧙‍♀️", "🧙‍♂️", "🧛‍♀️", "🧛‍♂️", "🧟‍♀️", "🧟‍♂️", "🧞‍♀️", "🧞‍♂️", "🧜‍♀️", "🧜‍♂️", + "🧝‍♀️", "🧝‍♂️", "🙍‍♀️", "🙍‍♂️", "🙎‍♀️", "🙎‍♂️", "🙅‍♀️", "🙅‍♂️", "🙆‍♀️", "🙆‍♂️", "💁‍♀️", "💁‍♂️", + "🙋‍♀️", "🙋‍♂️", "🧏‍♀️", "🧏‍♂️", "🙇‍♀️", "🙇‍♂️", "🤦‍♀️", "🤦‍♂️", "🤷‍♀️", "🤷‍♂️", "💇‍♀️", "💇‍♂️", + "💆‍♀️", "💆‍♂️", "🧖‍♀️", "🧖‍♂️", "🧘‍♀️", "🧘‍♂️", "🛌", "🧑‍🤝‍🧑", "👭", "👫", "👬", "💏", "💑", "👪", + + // Animals & Nature + "🐶", "🐱", "🐭", "🐹", "🐰", "🦊", "🐻", "🐼", "🐨", "🐯", "🦁", "🐮", "🐷", "🐽", "🐸", "🐵", + "🙈", "🙉", "🙊", "🐒", "🐔", "🐧", "🐦", "🐤", "🐣", "🐥", "🦆", "🦅", "🦉", "🦇", "🐺", "🐗", + "🐴", "🦄", "🐝", "🐛", "🦋", "🐌", "🐞", "🐜", "🦟", "🦗", "🐢", "🐍", "🦎", "🦂", "🦀", "🦞", + "🦑", "🐙", "🦐", "🐠", "🐟", "🐡", "🐬", "🐳", "🐋", "🦈", "🐊", "🐅", "🐆", "🦓", "🦍", "🦧", + "🐘", "🦛", "🦏", "🐪", "🐫", "🦒", "🦘", "🦥", "🦦", "🦨", "🦡", "🐁", "🐀", "🐇", "🦔", "🐿️", + "🦢", "🦚", "🦜", "🦩", "🕊️", "🐉", "🐲", "🌵", "🎄", "🌲", "🌳", "🌴", "🌱", "🌿", "☘️", "🍀", + "🎍", "🎋", "🍃", "🍂", "🍁", "🍄", "🌾", "💐", "🌷", "🌹", "🥀", "🌺", "🌸", "🌼", "🌻", "🌞", + + // Food & Drink + "🍇", "🍈", "🍉", "🍊", "🍋", "🍌", "🍍", "🥭", "🍎", "🍏", "🍐", "🍑", "🍒", "🍓", "🥝", "🍅", + "🥥", "🥑", "🍆", "🥔", "🥕", "🌽", "🌶️", "🥒", "🥬", "🥦", "🧄", "🧅", "🍄", "🥜", "🌰", "🍞", + "🥐", "🥖", "🥨", "🥯", "🥞", "🧇", "🧀", "🍖", "🍗", "🥩", "🥓", "🍔", "🍟", "🍕", "🌭", "🥪", + "🌮", "🌯", "🥙", "🧆", "🥚", "🍳", "🥘", "🍲", "🥣", "🥗", "🍿", "🧈", "🧂", "🥫", "🍱", "🍘", + "🍙", "🍚", "🍛", "🍜", "🍝", "🍠", "🍢", "🍣", "🍤", "🍥", "🥮", "🍡", "🥟", "🥠", "🥡", "🍦", + "🍧", "🍨", "🍩", "🍪", "🎂", "🍰", "🧁", "🥧", "🍫", "🍬", "🍭", "🍮", "🍯", "🍼", "🥛", "☕", + "🍵", "🍶", "🍾", "🍷", "🍸", "🍹", "🍺", "🍻", "🥂", "🥃", "🥤", "🧃", "🧉", "🧊", + + // Travel & Places + "🚗", "🚕", "🚙", "🚌", "🚎", "🏎️", "🚓", "🚑", "🚒", "🚐", "🚚", "🚛", "🚜", "🛴", "🚲", "🛵", + "🏍️", "🛺", "🚨", "🚔", "🚍", "🚘", "🚖", "🚡", "🚠", "🚟", "🚃", "🚋", "🚞", "🚝", "🚄", "🚅", + "🚈", "🚂", "🚆", "🚇", "🚊", "🚉", "✈️", "🛫", "🛬", "🛩️", "💺", "🛰️", "🚀", "🛸", "🚁", "🛶", + "⛵", "🚤", "🛥️", "🛳️", "⛴️", "🚢", "⚓", "⛽", "🚧", "🚦", "🚥", "🛑", "🚏", "🗺️", "🗿", "🗽", + "🗼", "🏰", "🏯", "🏟️", "🎡", "🎢", "🎠", "🏖️", "🏝️", "🏜️", "🌋", "🗻", "🏔️", "⛰️", "🏕️", + "🏠", "🏡", "🏘️", "🏚️", "🏢", "🏬", "🏭", "🏣", "🏤", "🏥", "🏦", "🏨", "🏪", "🏫", "🏩", "💒", + + // Symbols + "❤️", "🧡", "💛", "💚", "💙", "💜", "🖤", "🤍", "🤎", "💔", "❣️", "💕", "💞", "💓", "💗", "💖", + "💘", "💝", "💟", "☮️", "✝️", "☪️", "🕉️", "☸️", "✡️", "🔯", "🕎", "☯️", "☦️", "🛐", "⛎", "♈", + "♉", "♊", "♋", "♌", "♍", "♎", "♏", "♐", "♑", "♒", "♓", "🆔", "⚛️", "🉑", "☢️", "☣️", "📴", + "📳", "🈶", "🈚", "🈸", "🈺", "🈷️", "✴️", "🆚", "🉐", "💮", "🉑", "㊗️", "㊙️", "🈁", "🔞", "🉑" + }; +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/EmojiPicker.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/EmojiPicker.razor new file mode 100644 index 000000000..87237efc6 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/EmojiPicker.razor @@ -0,0 +1,37 @@ +@using MudBlazor + + + + + @foreach (var emoji in EmojiList.Emojis) + { + + + @emoji + + + } + + + + + + Emoji + + +@code { + [Parameter] public EventCallback OnEmojiSelected { get; set; } + + private bool IsEmojiPickerOpen { get; set; } + + private void ToggleEmojiPicker() + { + IsEmojiPickerOpen = !IsEmojiPickerOpen; + } + + private void SelectEmoji(string emoji) + { + IsEmojiPickerOpen = false; + OnEmojiSelected.InvokeAsync(emoji); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/MarkdownEditor.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/MarkdownEditor.razor new file mode 100644 index 000000000..0e9b375ac --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/Components/MarkdownEditor.razor @@ -0,0 +1,163 @@ +@using MudBlazor + + + + + + + @($"Characters: {MarkdownText.Length}/{CharacterLimit}") + + + + + + + Bold + + + + + Italic + + + + + Heading + + + + + Link + + + + + Code + + + + + + + Emoji + + + + + + + + + @foreach (var emoji in EmojiList.Emojis) + { + + + @emoji + + + } + + + + + + + +@code { + [Parameter] + public string MarkdownText { get; set; } = string.Empty; + + [Parameter] + public int CharacterLimit { get; set; } = 5000; // Default limit + + [Parameter] + public EventCallback MarkdownTextChanged { get; set; } + + [Inject] + IJSRuntime JSRuntime { get; set; } + + private bool IsEmojiPickerOpen { get; set; } = false; + + // Toggle emoji picker + private void ToggleEmojiPicker() + { + IsEmojiPickerOpen = !IsEmojiPickerOpen; + } + + // Insert emoji into the MarkdownText + private void InsertEmoji(string emoji) + { + MarkdownText += emoji; + IsEmojiPickerOpen = false; + MarkdownTextChanged.InvokeAsync(MarkdownText); + } + + // JavaScript interop to manipulate selected text + private async Task ApplyMarkdown(string action) + { + var selectedText = await JSRuntime.InvokeAsync("getSelectedText", "markdown-editor"); + + switch (action) + { + case "bold": + InsertText("**", "**", selectedText); + break; + case "italic": + InsertText("_", "_", selectedText); + break; + case "heading": + InsertText("### ", "", selectedText); + break; + case "link": + InsertText("[", "]()", selectedText); + break; + case "code": + InsertText("`", "`", selectedText); + break; + } + } + + private void InsertText(string prefix, string suffix, string selectedText) + { + if (!string.IsNullOrEmpty(selectedText)) + { + var newText = $"{prefix}{selectedText}{suffix}"; + MarkdownText = MarkdownText.Replace(selectedText, newText); + } + else + { + MarkdownText += $"{prefix}{suffix}"; + } + + MarkdownTextChanged.InvokeAsync(MarkdownText); + } + + // Get the style for the character counter based on the character limit + private string GetCharacterCounterStyle() + { + return MarkdownText.Length > CharacterLimit ? "color:red;" : "color:gray;"; + } + + // Get the color of the progress bar + private Color GetProgressColor() + { + return MarkdownText.Length > CharacterLimit ? Color.Error : Color.Primary; + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/CroppedCanvasDialog.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/CroppedCanvasDialog.razor new file mode 100644 index 000000000..3306ff34a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/CroppedCanvasDialog.razor @@ -0,0 +1,24 @@ +@using MudBlazor + + + + + Cropped canvas data + + + + + + + + + + + +@code { + [CascadingParameter] private MudDialogInstance MudDialog { get; set; } = null!; + + [Parameter] + [System.ComponentModel.DataAnnotations.Required] + public string Src { get; set; } = null!; +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/DisconnectedMessage.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/DisconnectedMessage.razor new file mode 100644 index 000000000..205bf8438 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/DisconnectedMessage.razor @@ -0,0 +1,3 @@ +
+

XXXXXXXXXXXXXXXYou have been disconnected. Please check your internet connection.

+
diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/DynamicLayoutComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/DynamicLayoutComponent.razor new file mode 100644 index 000000000..90b0e30aa --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/DynamicLayoutComponent.razor @@ -0,0 +1,37 @@ +@* @inherits LayoutComponentBase +@using Microsoft.AspNetCore.Components.Authorization +@inject AuthenticationStateProvider AuthenticationStateProvider + + + @if (_layoutComponent != null) + { + @(_layoutComponent) + } + + +@code { + private RenderFragment _layoutComponent; + + protected override async Task OnInitializedAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity.IsAuthenticated) + { + _layoutComponent = builder => + { + builder.OpenComponent(0, typeof(AuthenticatedLayout)); + builder.CloseComponent(); + }; + } + else + { + _layoutComponent = builder => + { + builder.OpenComponent(0, typeof(NotAuthenticatedLayout)); + builder.CloseComponent(); + }; + } + } + + +} *@ diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/FooterComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/FooterComponent.razor new file mode 100644 index 000000000..07136f38d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/FooterComponent.razor @@ -0,0 +1,61 @@ +@page "/footer" +@using MudBlazor +@inherits LayoutComponentBase + + + + + + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/MainLayout.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/MainLayout.razor new file mode 100644 index 000000000..aca662080 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/MainLayout.razor @@ -0,0 +1,377 @@ +@using MudBlazor +@using Astravent.Web.Wasm.Areas.Students +@inherits LayoutComponentBase +@inject IIdentityService IdentityService +@inject IStudentsService StudentsService +@inject NavigationManager NavigationManager +@using Blazored.LocalStorage +@inject ILocalStorageService localStorage +@inject AuthenticationStateProvider AuthenticationStateProvider + + + + + + @if (_isLoading) + { +
+ + Astravent... +
+ } + else + { + +
+ +
+ @if (_isUserAuthenticated) + { + + + MiniSpace + } + else + { + @if (!isPC) + { + + Menu + + MiniSpace + } + else + { + MiniSpace + + Home + + + About App + + + + Connect + Sign In + Sign Up + + } + } +
+ + + @if (_isUserAuthenticated) + { + + + + } + +
+ @if (_isUserAuthenticated) + { + + + } + else + { + + + Sign In + Sign Up + + } +
+
+
+ + + @if (!_isUserAuthenticated && !isPC) + { + + + Menu + + + Home + About App + Connect + Sign In + Sign Up + + + + } + + @if (_isUserAuthenticated) + { + + +
+ + @_userName +
+
+ + Profile + Settings + Account settings + Notifications + Sign Out + + +
+ } + + @if (_isUserAuthenticated ) + { + + + + Menu + + + + Home + Account + + + Follow + Search + Organize + My events + Recommended Events + + + + My posts + Create + Search + + + + Search + Friends + Requests + Sent Requests + + + + Search + My Organizations + Organizations I Follow + + + + All Chats + New Chat + Chat History + + + + All + New + History + + + Reports + @if (IdentityService.GetCurrentUserRole() == "admin") + { + + Students + Organizations + Reports + + } + + + } + + +
+ + @Body + + @if (_isUserAuthenticated) + { +
+ +
+ } + +
+ +
+ } +
+ + + +@code { + private bool _sidebarExpanded = false; + private bool _userDrawerOpen = false; + private bool isPC = true; + private bool _isUserAuthenticated; + private Guid _userId; + private string _userName; + private string _userEmail; + private Guid _studentId; + private string _studentState; + private string _userAvatar; + private bool _isLoading = true; + private string _searchQuery; + + [Inject] IJSRuntime JSRuntime { get; set; } + + protected override async Task OnInitializedAsync() + { + _isLoading = true; + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + _isUserAuthenticated = authState.User.Identity.IsAuthenticated; + + if (_isUserAuthenticated) + { + try + { + _userId = await IdentityService.GetCurrentUserIdFromJwtAsync(); + + var studentDto = await StudentsService.GetStudentAsync(_userId); + _userName = $"{studentDto.FirstName} {studentDto.LastName}"; + _userAvatar = studentDto.ProfileImageUrl; + } + catch (Exception ex) + { + Console.WriteLine($"Error initializing user data: {ex.Message}"); + } + } + + _isLoading = false; + StateHasChanged(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + _isLoading = true; + + _isUserAuthenticated = IdentityService.IsAuthenticated; + + if (_isUserAuthenticated) + { + StateHasChanged(); + } + + await UpdateScreenSize(); + _isLoading = false; // Finish loading + StateHasChanged(); // Trigger UI update + } + } + + + [JSInvokable] + public async Task UpdateScreenSize() + { + var screenSize = await JSRuntime.InvokeAsync("getScreenSize"); + isPC = screenSize.Width > 868; + StateHasChanged(); + } + + private class ScreenSize + { + public int Width { get; set; } + public int Height { get; set; } + } + + public async Task CheckAuthentication() + { + _isUserAuthenticated = await IdentityService.CheckIfUserIsAuthenticated(); + return _isUserAuthenticated; + } + + public bool IsUserAuthenticated => _isUserAuthenticated; + + private void ToggleDrawer() + { + _sidebarExpanded = !_sidebarExpanded; + } + + private void OpenDrawer() + { + _sidebarExpanded = true; + } + + private void ToggleUserDrawer() + { + _userDrawerOpen = !_userDrawerOpen; + } + + async Task SignOut() + { + if (_isUserAuthenticated && IdentityService.UserDto != null) + { + try + { + await IdentityService.UpdateStatus(IdentityService.UserDto.Id, isOnline: false, deviceType: "web"); + } + catch (Exception ex) + { + Console.WriteLine($"Error updating user status to offline: {ex.Message}"); + } + } + + await localStorage.ClearAsync(); + await IdentityService.Logout(); + + StateHasChanged(); + NavigationManager.NavigateTo("signin", forceLoad: true); + } + + private async Task ScrollToHome(MouseEventArgs e) + { + await ScrollToSection("home"); + } + + private async Task ScrollToAbout(MouseEventArgs e) + { + await ScrollToSection("about"); + } + + private async Task ScrollToConnect(MouseEventArgs e) + { + await ScrollToSection("connect"); + } + + async Task ScrollToSection(string sectionId) + { + if (NavigationManager.Uri != NavigationManager.BaseUri) + { + NavigationManager.NavigateTo("/", true); + } + while (NavigationManager.Uri != NavigationManager.BaseUri) + { + await Task.Delay(100); + } + await JSRuntime.InvokeVoidAsync("scrollToSection", sectionId); + } + + void NavigateToHome() + { + NavigationManager.NavigateTo("/home"); + } + + private string _themeName = "light"; + private string light = "light"; + private string dark = "dark"; + private string system = "system"; + private MudTheme _currentTheme; + private bool _isDarkMode; + + +} + + \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/NavMenu.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/NavMenu.razor new file mode 100644 index 000000000..f5af1b7e8 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/NavMenu.razor @@ -0,0 +1,68 @@ +@* @inject HttpClient Http *@ +@inject IIdentityService IdentityService +@inherits LayoutComponentBase +@inject NavigationManager NavigationManager +@inject Blazored.LocalStorage.ILocalStorageService LocalStorage + +@* + +
+ +
*@ + +@code { + private bool isUserAuthenticated; + private bool collapseNavMenu = true; + + protected override async Task OnInitializedAsync() + { + @* isUserAuthenticated = await LocalStorage.GetItemAsync("isAuthenticated"); + NavigationManager.LocationChanged += HandleLocationChanged; *@ + } + + private void HandleLocationChanged(object sender, LocationChangedEventArgs e) + { + @* InvokeAsync(() => + { + isUserAuthenticated = LocalStorage.GetItemAsync("isAuthenticated").Result; + StateHasChanged(); + }); *@ + } + + string NavMenuCssClass => collapseNavMenu ? "collapse" : null; + + void ToggleNavMenu() + { + collapseNavMenu = !collapseNavMenu; + } + + @* void LocationChanged(object sender, LocationChangedEventArgs e) + { + InvokeAsync(StateHasChanged); + } *@ +} + + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/NotificationComponent.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/NotificationComponent.razor new file mode 100644 index 000000000..be9d38d9a --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/NotificationComponent.razor @@ -0,0 +1,237 @@ +@using Astravent.Web.Wasm.Areas.Notifications +@using Astravent.Web.Wasm.DTO.Notifications +@using Microsoft.Extensions.Logging +@using System.Globalization +@inject INotificationsService NotificationsService +@inject IIdentityService IdentityService +@inject NavigationManager NavigationManager +@inject IJSRuntime JSRuntime +@inject ILogger Logger +@inject AuthenticationStateProvider AuthenticationStateProvider + + + + + + + @if (!showNotifications) + { +
+
+ N + @if (hasNewNotification) + { +
+ } +
+
+
+
+ } + +
+
+

Notifications

+ +
+
+ @if (notifications != null && notifications.Any()) + { +
    + @foreach (var notification in notifications) + { +
  • + + @RenderHtml(TruncateMessage(notification.Message)) + + + @notification.CreatedAt.ToLocalTime().ToString("f", CultureInfo.CurrentUICulture) + +
  • + } +
+ } + else + { +

No new notifications.

+ } +
+
+ +
+
+
+ + + +@code { + [Parameter] + public Guid UserId { get; set; } + + private List notifications = new List(); + private SignalRService SignalRService; + private Guid userId; + private bool showNotifications = false; + private bool hasNewNotification = false; + + protected override async Task OnInitializedAsync() + { + Console.WriteLine($"NotificationComponent Initialized for the {UserId}"); + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + + SignalRService = new SignalRService(NavigationManager, IdentityService); + SignalRService.NotificationReceived += OnNotificationReceived; + await SignalRService.StartAsync(UserId); + await LoadNotifications(); + + } + + + + private async Task LoadNotifications() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity.IsAuthenticated) + { + try + { + var paginatedResponse = await NotificationsService.GetNotificationsByUserAsync(UserId, pageSize: 15, sortOrder: "desc", status: "Unread"); + notifications = paginatedResponse.Results; + } + catch (Exception ex) + { + Logger.LogError($"Error loading notifications: {ex.Message}"); + } + } +} + + + private void OnNotificationReceived(NotificationDto notification) + { + var authState = AuthenticationStateProvider.GetAuthenticationStateAsync().Result; + if (authState.User.Identity.IsAuthenticated) + { + notifications.Insert(0, notification); + hasNewNotification = true; + InvokeAsync(StateHasChanged); + PlayNotificationSound(); + } + } + + + private void PlayNotificationSound() + { + JSRuntime.InvokeVoidAsync("playNotificationSound"); + } + + private void NavigateToNotificationDetail(Guid notificationId) + { + NavigationManager.NavigateTo($"/notification/{notificationId}", true); + } + + private string TruncateMessage(string message) + { + return message.Length > 80 ? message.Substring(0, 80) + "..." : message; + } + + private void ToggleNotifications() + { + showNotifications = !showNotifications; + if (showNotifications) + { + hasNewNotification = false; + } + } + + private MarkupString RenderHtml(string htmlContent) + { + return new MarkupString(htmlContent); + } + + public async ValueTask DisposeAsync() + { + var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync(); + if (authState.User.Identity.IsAuthenticated) + { + SignalRService.NotificationReceived -= OnNotificationReceived; + await SignalRService.StopAsync(); + } + } + +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/ReconnectMessage.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/ReconnectMessage.razor new file mode 100644 index 000000000..d2a07526e --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/ReconnectMessage.razor @@ -0,0 +1,8 @@ +
+

XXXXXXXXXXXXXXXXXXXAttempting to reconnect to the server: @CurrentAttempt of @MaxAttempts...

+
+ +@code { + [Parameter] public int CurrentAttempt { get; set; } + [Parameter] public int MaxAttempts { get; set; } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/themes/MiniSpaceTheme.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/themes/MiniSpaceTheme.cs new file mode 100644 index 000000000..96be5e862 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Shared/themes/MiniSpaceTheme.cs @@ -0,0 +1,47 @@ +using MudBlazor; + +public static class MiniSpaceTheme +{ + public static MudTheme MiniSpaceCustomTheme = new MudTheme() + { + Palette = new PaletteLight() + { + Primary = Colors.Indigo.Darken4, + Secondary = Colors.Indigo.Darken3, + AppbarText = Colors.Indigo.Darken4, + AppbarBackground = Colors.Shades.White, + Background = Colors.Grey.Lighten5, + Surface = "#FFFFFF", + DrawerBackground = Colors.Grey.Lighten3, + DrawerText = Colors.Grey.Darken3, + TextPrimary = Colors.Shades.Black, + TextSecondary = Colors.Grey.Darken2, + ActionDefault = Colors.Indigo.Lighten1, + ActionDisabled = Colors.Grey.Lighten2, + Success = Colors.Green.Default, + Warning = Colors.Orange.Default, + Error = Colors.Red.Default, + Info = Colors.Blue.Default + }, + PaletteDark = new PaletteDark() + { + // Dark Mode Colors + Primary = Colors.Indigo.Darken1, + Secondary = Colors.Blue.Darken4, + AppbarBackground = "#1F1F1F", // Dark app bar + Background = "#0d0f17", // Very dark background + Surface = "#212121", // Slightly lighter surface color + DrawerBackground = "#1B1B1B", // Dark drawer background + Dark = "#000000", // Dark drawer background + DrawerText = Colors.Grey.Lighten2, + TextPrimary = Colors.Shades.White, + TextSecondary = Colors.Grey.Lighten2, + ActionDefault = Colors.Indigo.Accent2, + ActionDisabled = Colors.BlueGrey.Darken3, + Success = Colors.Green.Accent3, + Warning = Colors.Orange.Accent3, + Error = Colors.Red.Accent3, + Info = Colors.Blue.Accent3 + } + }; +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/BrowserFile.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/BrowserFile.cs new file mode 100644 index 000000000..6c4939735 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/BrowserFile.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Components.Forms; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Utilities +{ + public class BrowserFile : IBrowserFile + { + private readonly byte[] _buffer; + + public BrowserFile(byte[] buffer, string name, string contentType, DateTimeOffset lastModified) + { + _buffer = buffer; + Name = name; + ContentType = contentType; + LastModified = lastModified; + } + + public string Name { get; } + public string ContentType { get; } + public long Size => _buffer.Length; + + // New property for LastModified + public DateTimeOffset LastModified { get; } + + public Stream OpenReadStream(long maxAllowedSize = 10 * 1024 * 1024, CancellationToken cancellationToken = default) + { + return new MemoryStream(_buffer); + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/IIPAddressService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/IIPAddressService.cs new file mode 100644 index 000000000..effabc1f5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/IIPAddressService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Astravent.Web.Wasm.Utilities +{ + public interface IIPAddressService + { + Task GetClientIpAddressAsync(); + } +} \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/IPAddressService.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/IPAddressService.cs new file mode 100644 index 000000000..caff8d055 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/IPAddressService.cs @@ -0,0 +1,39 @@ +using System.Net.Http; +using System.Threading.Tasks; +using System.Text.Json; + +namespace Astravent.Web.Wasm.Utilities +{ + public class IPAddressService : IIPAddressService + { + private readonly HttpClient _httpClient; + + public IPAddressService(HttpClient httpClient) + { + _httpClient = httpClient; + } + + public async Task GetClientIpAddressAsync() + { + try + { + Console.WriteLine("Getting IP address..."); + var response = await _httpClient.GetStringAsync("https://api.ipify.org?format=json"); + + var ipResponse = JsonSerializer.Deserialize(response); + Console.WriteLine($"IP address: {ipResponse?.Ip ?? "Unknown"}"); + + return ipResponse?.Ip ?? "Unknown"; + } + catch + { + return "Unknown"; + } + } + + private class IpResponse + { + public string Ip { get; set; } + } + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/QueryStringHelper.cs b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/QueryStringHelper.cs new file mode 100644 index 000000000..534473882 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/Utilities/QueryStringHelper.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Web; + +public static class QueryStringHelper +{ + public static string ToQueryString(object obj) + { + if (obj == null) return string.Empty; + + var properties = from p in obj.GetType().GetProperties() + where p.GetValue(obj, null) != null + select $"{HttpUtility.UrlEncode(p.Name)}={HttpUtility.UrlEncode(p.GetValue(obj, null).ToString())}"; + + return "?" + string.Join("&", properties.ToArray()); + } +} diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/_Imports.razor b/MiniSpace.Web/src/Astravent.Web.Wasm/_Imports.razor new file mode 100644 index 000000000..7b9dcc79d --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/_Imports.razor @@ -0,0 +1,63 @@ +@using System.Net.Http +@using System.Net.Http.Json + +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.JSInterop + +@using Astravent.Web.Wasm +@using Astravent.Web.Wasm.Shared +@using Astravent.Web.Wasm.Models.Identity +@using Astravent.Web.Wasm.Areas.Identity +@using Astravent.Web.Wasm.Areas.Students +@using Astravent.Web.Wasm.Areas.MediaFiles +@using Astravent.Web.Wasm.Areas.Organizations + +@using Cropper.Blazor.Components + +@using Astravent.Web.Wasm.DTO +@using Astravent.Web.Wasm.DTO.Enums +@using Astravent.Web.Wasm.DTO.Interests +@using Astravent.Web.Wasm.DTO.Languages +@using Astravent.Web.Wasm.DTO.Organizations +@using Astravent.Web.Wasm.DTO.Wrappers + +@using Astravent.Web.Wasm.Areas.Organizations.CommandsDto +@using Astravent.Web.Wasm.Areas.PagedResult +@using Astravent.Web.Wasm.Areas.Events +@using Astravent.Web.Wasm.DTO.Events +@using Astravent.Web.Wasm.Areas.Posts +@using Astravent.Web.Wasm.Areas.Events.CommandsDto + +@using Astravent.Web.Wasm.DTO.Enums.Reactions +@using Astravent.Web.Wasm.Areas.Reactions +@using Astravent.Web.Wasm.Data.Posts +@using Astravent.Web.Wasm.Areas.Reactions.CommandDto + +@using Astravent.Web.Wasm.Areas.Comments +@using Astravent.Web.Wasm.Areas.Comments.CommandsDto +@using Astravent.Web.Wasm.DTO.Comments + +@using Astravent.Web.Wasm.Areas.Friends.CommandsDto +@using Astravent.Web.Wasm.Areas.Friends +@using Astravent.Web.Wasm.DTO.Friends +@using Astravent.Web.Wasm.DTO.States + +@using Astravent.Web.Wasm.Areas.Communication +@using Astravent.Web.Wasm.DTO.Communication +@using Astravent.Web.Wasm.Areas.Communication.CommandsDto + +@using Astravent.Web.Wasm.DTO.Users +@using Astravent.Web.Wasm.Utilities +@using Astravent.Web.Wasm.DTO.Posts +@using Astravent.Web.Wasm.Shared.Components +@using MudBlazor + +@using Astravent.Web.Wasm.Areas.Reports +@using Astravent.Web.Wasm.Areas.Reports.CommandsDto + diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/wwwroot/css/bootstrap/bootstrap.min.css b/MiniSpace.Web/src/Astravent.Web.Wasm/wwwroot/css/bootstrap/bootstrap.min.css new file mode 100644 index 000000000..92e3fe871 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/wwwroot/css/bootstrap/bootstrap.min.css @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.3.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#6c757d;--gray-dark:#343a40;--primary:#007bff;--secondary:#6c757d;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#6c757d}.blockquote-footer::before{content:"\2014\00A0"}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#6c757d}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.table{width:100%;margin-bottom:1rem;color:#212529}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table-sm td,.table-sm th{padding:.3rem}.table-bordered{border:1px solid #dee2e6}.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{color:#212529;background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#b8daff}.table-primary tbody+tbody,.table-primary td,.table-primary th,.table-primary thead th{border-color:#7abaff}.table-hover .table-primary:hover{background-color:#9fcdff}.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#9fcdff}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#d6d8db}.table-secondary tbody+tbody,.table-secondary td,.table-secondary th,.table-secondary thead th{border-color:#b3b7bb}.table-hover .table-secondary:hover{background-color:#c8cbcf}.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#c8cbcf}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-success tbody+tbody,.table-success td,.table-success th,.table-success thead th{border-color:#8fd19e}.table-hover .table-success:hover{background-color:#b1dfbb}.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-info tbody+tbody,.table-info td,.table-info th,.table-info thead th{border-color:#86cfda}.table-hover .table-info:hover{background-color:#abdde5}.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-warning tbody+tbody,.table-warning td,.table-warning th,.table-warning thead th{border-color:#ffdf7e}.table-hover .table-warning:hover{background-color:#ffe8a1}.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-danger tbody+tbody,.table-danger td,.table-danger th,.table-danger thead th{border-color:#ed969e}.table-hover .table-danger:hover{background-color:#f1b0b7}.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-light tbody+tbody,.table-light td,.table-light th,.table-light thead th{border-color:#fbfcfc}.table-hover .table-light:hover{background-color:#ececf6}.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#95999c}.table-hover .table-dark:hover{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#343a40;border-color:#454d55}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#343a40}.table-dark td,.table-dark th,.table-dark thead th{border-color:#454d55}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.form-control::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#6c757d}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.form-control.is-valid,.was-validated .form-control:valid{border-color:#28a745;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-valid,.was-validated .custom-select:valid{border-color:#28a745;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-valid:focus,.was-validated .custom-select:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip{display:block}.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label::before,.was-validated .custom-control-input:valid~.custom-control-label::before{border-color:#28a745}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label::before,.was-validated .custom-control-input:valid:checked~.custom-control-label::before{border-color:#34ce57;background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label::before,.was-validated .custom-control-input:valid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E");background-repeat:no-repeat;background-position:center right calc(.375em + .1875rem);background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.custom-select.is-invalid,.was-validated .custom-select:invalid{border-color:#dc3545;padding-right:calc((1em + .75rem) * 3 / 4 + 1.75rem);background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px,url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E") #fff no-repeat center right 1.75rem/calc(.75em + .375rem) calc(.75em + .375rem)}.custom-select.is-invalid:focus,.was-validated .custom-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip{display:block}.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label::before,.was-validated .custom-control-input:invalid~.custom-control-label::before{border-color:#dc3545}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label::before,.was-validated .custom-control-input:invalid:checked~.custom-control-label::before{border-color:#e4606d;background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;color:#212529;text-align:center;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:transparent;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:#212529;text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.btn.disabled,.btn:disabled{opacity:.65}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#fff;background-color:#007bff;border-color:#007bff}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#fff;background-color:#0062cc;border-color:#005cbf}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(38,143,255,.5)}.btn-secondary{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#545b62;border-color:#4e555b}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(130,138,145,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(72,180,97,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(58,176,195,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(222,170,12,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(225,83,97,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(216,217,219,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.btn-outline-primary{color:#007bff;border-color:#007bff}.btn-outline-primary:hover{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#007bff;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#fff;background-color:#007bff;border-color:#007bff}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.btn-outline-secondary{color:#6c757d;border-color:#6c757d}.btn-outline-secondary:hover{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#6c757d;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#6c757d}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.btn-outline-success{color:#28a745;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#007bff;text-decoration:none}.btn-link:hover{color:#0056b3;text-decoration:underline}.btn-link.focus,.btn-link:focus{text-decoration:underline;box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#6c757d;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-left{right:auto;left:0}.dropdown-menu-right{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-left{right:auto;left:0}.dropdown-menu-lg-right{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-left{right:auto;left:0}.dropdown-menu-xl-right{right:0;left:auto}}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty::after{margin-left:0}.dropright .dropdown-toggle::after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropleft .dropdown-toggle::after{display:none}.dropleft .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty::after{margin-left:0}.dropleft .dropdown-toggle::before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#007bff}.dropdown-item.disabled,.dropdown-item:disabled{color:#6c757d;pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#6c757d;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropright .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropleft .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control,.input-group>.form-control-plaintext+.custom-file,.input-group>.form-control-plaintext+.custom-select,.input-group>.form-control-plaintext+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label::after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn:focus,.input-group-prepend .btn:focus{z-index:3}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.custom-select,.input-group-lg>.form-control:not(textarea){height:calc(1.5em + 1rem + 2px)}.input-group-lg>.custom-select,.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.custom-select,.input-group-sm>.form-control:not(textarea){height:calc(1.5em + .5rem + 2px)}.input-group-sm>.custom-select,.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group-lg>.custom-select,.input-group-sm>.custom-select{padding-right:1.75rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label::before{color:#fff;border-color:#007bff;background-color:#007bff}.custom-control-input:focus~.custom-control-label::before{box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-control-input:focus:not(:checked)~.custom-control-label::before{border-color:#80bdff}.custom-control-input:not(:disabled):active~.custom-control-label::before{color:#fff;background-color:#b3d7ff;border-color:#b3d7ff}.custom-control-input:disabled~.custom-control-label{color:#6c757d}.custom-control-input:disabled~.custom-control-label::before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0;vertical-align:top}.custom-control-label::before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";background:no-repeat 50%/50% 50%}.custom-checkbox .custom-control-label::before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before{border-color:#007bff;background-color:#007bff}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-radio .custom-control-label::before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label::after{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-switch{padding-left:2.25rem}.custom-switch .custom-control-label::before{left:-2.25rem;width:1.75rem;pointer-events:all;border-radius:.5rem}.custom-switch .custom-control-label::after{top:calc(.25rem + 2px);left:calc(-2.25rem + 2px);width:calc(1rem - 4px);height:calc(1rem - 4px);background-color:#adb5bd;border-radius:.5rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-switch .custom-control-label::after{transition:none}}.custom-switch .custom-control-input:checked~.custom-control-label::after{background-color:#fff;-webkit-transform:translateX(.75rem);transform:translateX(.75rem)}.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before{background-color:rgba(0,123,255,.5)}.custom-select{display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);padding:.375rem 1.75rem .375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;vertical-align:middle;background:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right .75rem center/8px 10px;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#6c757d;background-color:#e9ecef}.custom-select::-ms-expand{display:none}.custom-select-sm{height:calc(1.5em + .5rem + 2px);padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem}.custom-select-lg{height:calc(1.5em + 1rem + 2px);padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem}.custom-file{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label::after{content:"Browse"}.custom-file-input~.custom-file-label[data-browse]::after{content:attr(data-browse)}.custom-file-label{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;content:"Browse";background-color:#e9ecef;border-left:inherit;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;height:calc(1rem + .4rem);padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(0,123,255,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#b3d7ff}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#b3d7ff}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#007bff;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#b3d7ff}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.custom-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.custom-range:disabled::-webkit-slider-runnable-track{cursor:default}.custom-range:disabled::-moz-range-thumb{background-color:#adb5bd}.custom-range:disabled::-moz-range-track{cursor:default}.custom-range:disabled::-ms-thumb{background-color:#adb5bd}.custom-control-label::before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.custom-control-label::before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#6c757d;pointer-events:none;cursor:default}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#6c757d;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#007bff}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat center center;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion>.card{overflow:hidden}.accordion>.card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion>.card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion>.card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion>.card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.accordion>.card .card-header{margin-bottom:-1px}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item::before{display:inline-block;padding-right:.5rem;color:#6c757d;content:"/"}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:underline}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#007bff;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#0056b3;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#007bff;border-color:#007bff}.page-item.disabled .page-link{color:#6c757d;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.badge{transition:none}}a.badge:focus,a.badge:hover{text-decoration:none}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#fff;background-color:#007bff}a.badge-primary:focus,a.badge-primary:hover{color:#fff;background-color:#0062cc}a.badge-primary.focus,a.badge-primary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(0,123,255,.5)}.badge-secondary{color:#fff;background-color:#6c757d}a.badge-secondary:focus,a.badge-secondary:hover{color:#fff;background-color:#545b62}a.badge-secondary.focus,a.badge-secondary:focus{outline:0;box-shadow:0 0 0 .2rem rgba(108,117,125,.5)}.badge-success{color:#fff;background-color:#28a745}a.badge-success:focus,a.badge-success:hover{color:#fff;background-color:#1e7e34}a.badge-success.focus,a.badge-success:focus{outline:0;box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.badge-info{color:#fff;background-color:#17a2b8}a.badge-info:focus,a.badge-info:hover{color:#fff;background-color:#117a8b}a.badge-info.focus,a.badge-info:focus{outline:0;box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.badge-warning{color:#212529;background-color:#ffc107}a.badge-warning:focus,a.badge-warning:hover{color:#212529;background-color:#d39e00}a.badge-warning.focus,a.badge-warning:focus{outline:0;box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.badge-danger{color:#fff;background-color:#dc3545}a.badge-danger:focus,a.badge-danger:hover{color:#fff;background-color:#bd2130}a.badge-danger.focus,a.badge-danger:focus{outline:0;box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.badge-light{color:#212529;background-color:#f8f9fa}a.badge-light:focus,a.badge-light:hover{color:#212529;background-color:#dae0e5}a.badge-light.focus,a.badge-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.badge-dark{color:#fff;background-color:#343a40}a.badge-dark:focus,a.badge-dark:hover{color:#fff;background-color:#1d2124}a.badge-dark.focus,a.badge-dark:focus{outline:0;box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#004085;background-color:#cce5ff;border-color:#b8daff}.alert-primary hr{border-top-color:#9fcdff}.alert-primary .alert-link{color:#002752}.alert-secondary{color:#383d41;background-color:#e2e3e5;border-color:#d6d8db}.alert-secondary hr{border-top-color:#c8cbcf}.alert-secondary .alert-link{color:#202326}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#007bff;transition:width .6s ease}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item.disabled,.list-group-item:disabled{color:#6c757d;pointer-events:none;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#007bff;border-color:#007bff}.list-group-horizontal{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}@media (min-width:576px){.list-group-horizontal-sm{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-sm .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-sm .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-sm .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:768px){.list-group-horizontal-md{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-md .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-md .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-md .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:992px){.list-group-horizontal-lg{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-lg .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-lg .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-lg .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}@media (min-width:1200px){.list-group-horizontal-xl{-ms-flex-direction:row;flex-direction:row}.list-group-horizontal-xl .list-group-item{margin-right:-1px;margin-bottom:0}.list-group-horizontal-xl .list-group-item:first-child{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem;border-top-right-radius:0}.list-group-horizontal-xl .list-group-item:last-child{margin-right:0;border-top-right-radius:.25rem;border-bottom-right-radius:.25rem;border-bottom-left-radius:0}}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush .list-group-item:last-child{margin-bottom:-1px}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{margin-bottom:0;border-bottom:0}.list-group-item-primary{color:#004085;background-color:#b8daff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#004085;background-color:#9fcdff}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#004085;border-color:#004085}.list-group-item-secondary{color:#383d41;background-color:#d6d8db}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#383d41;background-color:#c8cbcf}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#383d41;border-color:#383d41}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:hover{color:#000;text-decoration:none}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}a.close.disabled{pointer-events:none}.toast{max-width:350px;overflow:hidden;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);opacity:0;border-radius:.25rem}.toast:not(:last-child){margin-bottom:.75rem}.toast.showing{opacity:1}.toast.show{display:block;opacity:1}.toast.hide{display:none}.toast-header{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05)}.toast-body{padding:.75rem}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;left:0;z-index:1050;display:none;width:100%;height:100%;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal-dialog-scrollable{display:-ms-flexbox;display:flex;max-height:calc(100% - 1rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 1rem);overflow:hidden}.modal-dialog-scrollable .modal-footer,.modal-dialog-scrollable .modal-header{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.modal-dialog-centered.modal-dialog-scrollable .modal-content{max-height:none}.modal-dialog-centered.modal-dialog-scrollable::before{content:none}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 1rem;border-bottom:1px solid #dee2e6;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #dee2e6;border-bottom-right-radius:.3rem;border-bottom-left-radius:.3rem}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-scrollable{max-height:calc(100% - 3.5rem)}.modal-dialog-scrollable .modal-content{max-height:calc(100vh - 3.5rem)}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered::before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{max-width:800px}}@media (min-width:1200px){.modal-xl{max-width:1140px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow::before,.bs-tooltip-top .arrow::before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow::before,.bs-tooltip-right .arrow::before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow::before,.bs-tooltip-bottom .arrow::before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow::before,.bs-tooltip-left .arrow::before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{position:absolute;top:0;left:0;z-index:1060;display:block;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover .arrow{position:absolute;display:block;width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow::after,.popover .arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top]>.arrow,.bs-popover-top>.arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top]>.arrow::before,.bs-popover-top>.arrow::before{bottom:0;border-width:.5rem .5rem 0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top]>.arrow::after,.bs-popover-top>.arrow::after{bottom:1px;border-width:.5rem .5rem 0;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right]>.arrow,.bs-popover-right>.arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right]>.arrow::before,.bs-popover-right>.arrow::before{left:0;border-width:.5rem .5rem .5rem 0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right]>.arrow::after,.bs-popover-right>.arrow::after{left:1px;border-width:.5rem .5rem .5rem 0;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom]>.arrow,.bs-popover-bottom>.arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom]>.arrow::before,.bs-popover-bottom>.arrow::before{top:0;border-width:0 .5rem .5rem .5rem;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom]>.arrow::after,.bs-popover-bottom>.arrow::after{top:1px;border-width:0 .5rem .5rem .5rem;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left]>.arrow,.bs-popover-left>.arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left]>.arrow::before,.bs-popover-left>.arrow::before{right:0;border-width:.5rem 0 .5rem .5rem;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left]>.arrow::after,.bs-popover-left>.arrow::after{right:1px;border-width:.5rem 0 .5rem .5rem;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel.pointer-event{-ms-touch-action:pan-y;touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-right,.carousel-item-next:not(.carousel-item-left){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;transform:none}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{z-index:0;opacity:0;transition:0s .6s opacity}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:no-repeat 50%/100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators li{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;animation:spinner-grow .75s linear infinite}.spinner-grow-sm{width:1rem;height:1rem}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#007bff!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#0062cc!important}.bg-secondary{background-color:#6c757d!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#545b62!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#007bff!important}.border-secondary{border-color:#6c757d!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded-sm{border-radius:.2rem!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-lg{border-radius:.3rem!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:50rem!important}.rounded-0{border-radius:0!important}.clearfix::after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!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:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!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:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!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:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!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:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!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:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive::before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9::before{padding-top:42.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.min-vw-100{min-width:100vw!important}.min-vh-100{min-height:100vh!important}.vw-100{width:100vw!important}.vh-100{height:100vh!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;pointer-events:auto;content:"";background-color:rgba(0,0,0,0)}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-n1{margin:-.25rem!important}.mt-n1,.my-n1{margin-top:-.25rem!important}.mr-n1,.mx-n1{margin-right:-.25rem!important}.mb-n1,.my-n1{margin-bottom:-.25rem!important}.ml-n1,.mx-n1{margin-left:-.25rem!important}.m-n2{margin:-.5rem!important}.mt-n2,.my-n2{margin-top:-.5rem!important}.mr-n2,.mx-n2{margin-right:-.5rem!important}.mb-n2,.my-n2{margin-bottom:-.5rem!important}.ml-n2,.mx-n2{margin-left:-.5rem!important}.m-n3{margin:-1rem!important}.mt-n3,.my-n3{margin-top:-1rem!important}.mr-n3,.mx-n3{margin-right:-1rem!important}.mb-n3,.my-n3{margin-bottom:-1rem!important}.ml-n3,.mx-n3{margin-left:-1rem!important}.m-n4{margin:-1.5rem!important}.mt-n4,.my-n4{margin-top:-1.5rem!important}.mr-n4,.mx-n4{margin-right:-1.5rem!important}.mb-n4,.my-n4{margin-bottom:-1.5rem!important}.ml-n4,.mx-n4{margin-left:-1.5rem!important}.m-n5{margin:-3rem!important}.mt-n5,.my-n5{margin-top:-3rem!important}.mr-n5,.mx-n5{margin-right:-3rem!important}.mb-n5,.my-n5{margin-bottom:-3rem!important}.ml-n5,.mx-n5{margin-left:-3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-n1{margin:-.25rem!important}.mt-sm-n1,.my-sm-n1{margin-top:-.25rem!important}.mr-sm-n1,.mx-sm-n1{margin-right:-.25rem!important}.mb-sm-n1,.my-sm-n1{margin-bottom:-.25rem!important}.ml-sm-n1,.mx-sm-n1{margin-left:-.25rem!important}.m-sm-n2{margin:-.5rem!important}.mt-sm-n2,.my-sm-n2{margin-top:-.5rem!important}.mr-sm-n2,.mx-sm-n2{margin-right:-.5rem!important}.mb-sm-n2,.my-sm-n2{margin-bottom:-.5rem!important}.ml-sm-n2,.mx-sm-n2{margin-left:-.5rem!important}.m-sm-n3{margin:-1rem!important}.mt-sm-n3,.my-sm-n3{margin-top:-1rem!important}.mr-sm-n3,.mx-sm-n3{margin-right:-1rem!important}.mb-sm-n3,.my-sm-n3{margin-bottom:-1rem!important}.ml-sm-n3,.mx-sm-n3{margin-left:-1rem!important}.m-sm-n4{margin:-1.5rem!important}.mt-sm-n4,.my-sm-n4{margin-top:-1.5rem!important}.mr-sm-n4,.mx-sm-n4{margin-right:-1.5rem!important}.mb-sm-n4,.my-sm-n4{margin-bottom:-1.5rem!important}.ml-sm-n4,.mx-sm-n4{margin-left:-1.5rem!important}.m-sm-n5{margin:-3rem!important}.mt-sm-n5,.my-sm-n5{margin-top:-3rem!important}.mr-sm-n5,.mx-sm-n5{margin-right:-3rem!important}.mb-sm-n5,.my-sm-n5{margin-bottom:-3rem!important}.ml-sm-n5,.mx-sm-n5{margin-left:-3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-n1{margin:-.25rem!important}.mt-md-n1,.my-md-n1{margin-top:-.25rem!important}.mr-md-n1,.mx-md-n1{margin-right:-.25rem!important}.mb-md-n1,.my-md-n1{margin-bottom:-.25rem!important}.ml-md-n1,.mx-md-n1{margin-left:-.25rem!important}.m-md-n2{margin:-.5rem!important}.mt-md-n2,.my-md-n2{margin-top:-.5rem!important}.mr-md-n2,.mx-md-n2{margin-right:-.5rem!important}.mb-md-n2,.my-md-n2{margin-bottom:-.5rem!important}.ml-md-n2,.mx-md-n2{margin-left:-.5rem!important}.m-md-n3{margin:-1rem!important}.mt-md-n3,.my-md-n3{margin-top:-1rem!important}.mr-md-n3,.mx-md-n3{margin-right:-1rem!important}.mb-md-n3,.my-md-n3{margin-bottom:-1rem!important}.ml-md-n3,.mx-md-n3{margin-left:-1rem!important}.m-md-n4{margin:-1.5rem!important}.mt-md-n4,.my-md-n4{margin-top:-1.5rem!important}.mr-md-n4,.mx-md-n4{margin-right:-1.5rem!important}.mb-md-n4,.my-md-n4{margin-bottom:-1.5rem!important}.ml-md-n4,.mx-md-n4{margin-left:-1.5rem!important}.m-md-n5{margin:-3rem!important}.mt-md-n5,.my-md-n5{margin-top:-3rem!important}.mr-md-n5,.mx-md-n5{margin-right:-3rem!important}.mb-md-n5,.my-md-n5{margin-bottom:-3rem!important}.ml-md-n5,.mx-md-n5{margin-left:-3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-n1{margin:-.25rem!important}.mt-lg-n1,.my-lg-n1{margin-top:-.25rem!important}.mr-lg-n1,.mx-lg-n1{margin-right:-.25rem!important}.mb-lg-n1,.my-lg-n1{margin-bottom:-.25rem!important}.ml-lg-n1,.mx-lg-n1{margin-left:-.25rem!important}.m-lg-n2{margin:-.5rem!important}.mt-lg-n2,.my-lg-n2{margin-top:-.5rem!important}.mr-lg-n2,.mx-lg-n2{margin-right:-.5rem!important}.mb-lg-n2,.my-lg-n2{margin-bottom:-.5rem!important}.ml-lg-n2,.mx-lg-n2{margin-left:-.5rem!important}.m-lg-n3{margin:-1rem!important}.mt-lg-n3,.my-lg-n3{margin-top:-1rem!important}.mr-lg-n3,.mx-lg-n3{margin-right:-1rem!important}.mb-lg-n3,.my-lg-n3{margin-bottom:-1rem!important}.ml-lg-n3,.mx-lg-n3{margin-left:-1rem!important}.m-lg-n4{margin:-1.5rem!important}.mt-lg-n4,.my-lg-n4{margin-top:-1.5rem!important}.mr-lg-n4,.mx-lg-n4{margin-right:-1.5rem!important}.mb-lg-n4,.my-lg-n4{margin-bottom:-1.5rem!important}.ml-lg-n4,.mx-lg-n4{margin-left:-1.5rem!important}.m-lg-n5{margin:-3rem!important}.mt-lg-n5,.my-lg-n5{margin-top:-3rem!important}.mr-lg-n5,.mx-lg-n5{margin-right:-3rem!important}.mb-lg-n5,.my-lg-n5{margin-bottom:-3rem!important}.ml-lg-n5,.mx-lg-n5{margin-left:-3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-n1{margin:-.25rem!important}.mt-xl-n1,.my-xl-n1{margin-top:-.25rem!important}.mr-xl-n1,.mx-xl-n1{margin-right:-.25rem!important}.mb-xl-n1,.my-xl-n1{margin-bottom:-.25rem!important}.ml-xl-n1,.mx-xl-n1{margin-left:-.25rem!important}.m-xl-n2{margin:-.5rem!important}.mt-xl-n2,.my-xl-n2{margin-top:-.5rem!important}.mr-xl-n2,.mx-xl-n2{margin-right:-.5rem!important}.mb-xl-n2,.my-xl-n2{margin-bottom:-.5rem!important}.ml-xl-n2,.mx-xl-n2{margin-left:-.5rem!important}.m-xl-n3{margin:-1rem!important}.mt-xl-n3,.my-xl-n3{margin-top:-1rem!important}.mr-xl-n3,.mx-xl-n3{margin-right:-1rem!important}.mb-xl-n3,.my-xl-n3{margin-bottom:-1rem!important}.ml-xl-n3,.mx-xl-n3{margin-left:-1rem!important}.m-xl-n4{margin:-1.5rem!important}.mt-xl-n4,.my-xl-n4{margin-top:-1.5rem!important}.mr-xl-n4,.mx-xl-n4{margin-right:-1.5rem!important}.mb-xl-n4,.my-xl-n4{margin-bottom:-1.5rem!important}.ml-xl-n4,.mx-xl-n4{margin-left:-1.5rem!important}.m-xl-n5{margin:-3rem!important}.mt-xl-n5,.my-xl-n5{margin-top:-3rem!important}.mr-xl-n5,.mx-xl-n5{margin-right:-3rem!important}.mb-xl-n5,.my-xl-n5{margin-bottom:-3rem!important}.ml-xl-n5,.mx-xl-n5{margin-left:-3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace!important}.text-justify{text-align:justify!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-lighter{font-weight:lighter!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-weight-bolder{font-weight:bolder!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}.text-primary{color:#007bff!important}a.text-primary:focus,a.text-primary:hover{color:#0056b3!important}.text-secondary{color:#6c757d!important}a.text-secondary:focus,a.text-secondary:hover{color:#494f54!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#19692c!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#0f6674!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#ba8b00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#a71d2a!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#cbd3da!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#121416!important}.text-body{color:#212529!important}.text-muted{color:#6c757d!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:rgba(255,255,255,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.text-decoration-none{text-decoration:none!important}.text-break{word-break:break-word!important;overflow-wrap:break-word!important}.text-reset{color:inherit!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,::after,::before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]::after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}body{min-width:992px!important}.container{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/MiniSpace.Web/src/Astravent.Web.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map b/MiniSpace.Web/src/Astravent.Web.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map new file mode 100644 index 000000000..1e9cb78a5 --- /dev/null +++ b/MiniSpace.Web/src/Astravent.Web.Wasm/wwwroot/css/bootstrap/bootstrap.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","bootstrap.css","../../scss/mixins/_hover.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/mixins/_border-radius.scss","../../scss/_code.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/mixins/_breakpoints.scss","../../scss/mixins/_grid-framework.scss","../../scss/_tables.scss","../../scss/mixins/_table-row.scss","../../scss/_forms.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_forms.scss","../../scss/mixins/_gradients.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/mixins/_nav-divider.scss","../../scss/_button-group.scss","../../scss/_input-group.scss","../../scss/_custom-forms.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/mixins/_badge.scss","../../scss/_jumbotron.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_media.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/utilities/_align.scss","../../scss/mixins/_background-variant.scss","../../scss/utilities/_background.scss","../../scss/utilities/_borders.scss","../../scss/utilities/_display.scss","../../scss/utilities/_embed.scss","../../scss/utilities/_flex.scss","../../scss/utilities/_float.scss","../../scss/utilities/_overflow.scss","../../scss/utilities/_position.scss","../../scss/utilities/_screenreaders.scss","../../scss/mixins/_screen-reader.scss","../../scss/utilities/_shadows.scss","../../scss/utilities/_sizing.scss","../../scss/utilities/_stretched-link.scss","../../scss/utilities/_spacing.scss","../../scss/utilities/_text.scss","../../scss/mixins/_text-truncate.scss","../../scss/mixins/_text-emphasis.scss","../../scss/mixins/_text-hide.scss","../../scss/utilities/_visibility.scss","../../scss/_print.scss"],"names":[],"mappings":"AAAA;;;;;ACAA,MAGI,OAAA,QAAA,SAAA,QAAA,SAAA,QAAA,OAAA,QAAA,MAAA,QAAA,SAAA,QAAA,SAAA,QAAA,QAAA,QAAA,OAAA,QAAA,OAAA,QAAA,QAAA,KAAA,OAAA,QAAA,YAAA,QAIA,UAAA,QAAA,YAAA,QAAA,UAAA,QAAA,OAAA,QAAA,UAAA,QAAA,SAAA,QAAA,QAAA,QAAA,OAAA,QAIA,gBAAA,EAAA,gBAAA,MAAA,gBAAA,MAAA,gBAAA,MAAA,gBAAA,OAKF,yBAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBACA,wBAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UCCF,ECqBA,QADA,SDjBE,WAAA,WAGF,KACE,YAAA,WACA,YAAA,KACA,yBAAA,KACA,4BAAA,YAMF,QAAA,MAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,IAAA,QACE,QAAA,MAUF,KACE,OAAA,EACA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBEgFI,UAAA,KF9EJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,KACA,iBAAA,KGYF,sBHHE,QAAA,YASF,GACE,WAAA,YACA,OAAA,EACA,SAAA,QAaF,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAOF,EACE,WAAA,EACA,cAAA,KCZF,0BDuBA,YAEE,gBAAA,UACA,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,cAAA,EACA,iCAAA,KAAA,yBAAA,KAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QCjBF,GDoBA,GCrBA,GDwBE,WAAA,EACA,cAAA,KAGF,MCpBA,MACA,MAFA,MDyBE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAGF,ECrBA,ODuBE,YAAA,OAGF,MEpFI,UAAA,IF6FJ,IC1BA,ID4BE,SAAA,SE/FE,UAAA,IFiGF,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAON,EACE,MAAA,QACA,gBAAA,KACA,iBAAA,YI5KA,QJ+KE,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KIxLA,oCAAA,oCJ2LE,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EC5BJ,KACA,IDoCA,ICnCA,KDuCE,YAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UErJE,UAAA,IFyJJ,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAQF,OAEE,OAAA,EAAA,EAAA,KAQF,IACE,eAAA,OACA,aAAA,KAGF,IAGE,SAAA,OACA,eAAA,OAQF,MACE,gBAAA,SAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAGE,WAAA,QAQF,MAEE,QAAA,aACA,cAAA,MAMF,OAEE,cAAA,EAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBCvEF,OD0EA,MCxEA,SADA,OAEA,SD4EE,OAAA,EACA,YAAA,QEtPE,UAAA,QFwPF,YAAA,QAGF,OC1EA,MD4EE,SAAA,QAGF,OC1EA,OD4EE,eAAA,KAMF,OACE,UAAA,OC1EF,cACA,aACA,cD+EA,OAIE,mBAAA,OC9EF,6BACA,4BACA,6BDiFE,sBAKI,OAAA,QCjFN,gCACA,+BACA,gCDqFA,yBAIE,QAAA,EACA,aAAA,KCpFF,qBDuFA,kBAEE,WAAA,WACA,QAAA,EAIF,iBCvFA,2BACA,kBAFA,iBDiGE,mBAAA,QAGF,SACE,SAAA,KAEA,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAKF,OACE,QAAA,MACA,MAAA,KACA,UAAA,KACA,QAAA,EACA,cAAA,MElSI,UAAA,OFoSJ,YAAA,QACA,MAAA,QACA,YAAA,OAGF,SACE,eAAA,SGtGF,yCFGA,yCDyGE,OAAA,KGvGF,cH+GE,eAAA,KACA,mBAAA,KG3GF,yCHmHE,mBAAA,KAQF,6BACE,KAAA,QACA,mBAAA,OAOF,OACE,QAAA,aAGF,QACE,QAAA,UACA,OAAA,QAGF,SACE,QAAA,KGxHF,SH8HE,QAAA,eCvHF,IAAK,IAAK,IAAK,IAAK,IAAK,IIpWzB,GAAA,GAAA,GAAA,GAAA,GAAA,GAEE,cAAA,MAEA,YAAA,IACA,YAAA,IAIF,IAAA,GHgHM,UAAA,OG/GN,IAAA,GH+GM,UAAA,KG9GN,IAAA,GH8GM,UAAA,QG7GN,IAAA,GH6GM,UAAA,OG5GN,IAAA,GH4GM,UAAA,QG3GN,IAAA,GH2GM,UAAA,KGzGN,MHyGM,UAAA,QGvGJ,YAAA,IAIF,WHmGM,UAAA,KGjGJ,YAAA,IACA,YAAA,IAEF,WH8FM,UAAA,OG5FJ,YAAA,IACA,YAAA,IAEF,WHyFM,UAAA,OGvFJ,YAAA,IACA,YAAA,IAEF,WHoFM,UAAA,OGlFJ,YAAA,IACA,YAAA,ILyBF,GKhBE,WAAA,KACA,cAAA,KACA,OAAA,EACA,WAAA,IAAA,MAAA,eJmXF,OI3WA,MHMI,UAAA,IGHF,YAAA,IJ8WF,MI3WA,KAEE,QAAA,KACA,iBAAA,QAQF,eC/EE,aAAA,EACA,WAAA,KDmFF,aCpFE,aAAA,EACA,WAAA,KDsFF,kBACE,QAAA,aADF,mCAII,aAAA,MAUJ,YHjCI,UAAA,IGmCF,eAAA,UAIF,YACE,cAAA,KHeI,UAAA,QGXN,mBACE,QAAA,MH7CE,UAAA,IG+CF,MAAA,QAHF,2BAMI,QAAA,aEnHJ,WCIE,UAAA,KAGA,OAAA,KDDF,eACE,QAAA,OACA,iBAAA,KACA,OAAA,IAAA,MAAA,QEXE,cAAA,ODMF,UAAA,KAGA,OAAA,KDcF,QAEE,QAAA,aAGF,YACE,cAAA,MACA,YAAA,EAGF,gBLkCI,UAAA,IKhCF,MAAA,QGvCF,KRuEI,UAAA,MQrEF,MAAA,QACA,WAAA,WAGA,OACE,MAAA,QAKJ,IACE,QAAA,MAAA,MR0DE,UAAA,MQxDF,MAAA,KACA,iBAAA,QDZE,cAAA,MCQJ,QASI,QAAA,ERkDA,UAAA,KQhDA,YAAA,IVyMJ,IUlME,QAAA,MRyCE,UAAA,MQvCF,MAAA,QAHF,SR0CI,UAAA,QQlCA,MAAA,QACA,WAAA,OAKJ,gBACE,WAAA,MACA,WAAA,OCzCA,WCAA,MAAA,KACA,cAAA,KACA,aAAA,KACA,aAAA,KACA,YAAA,KCmDE,yBFvDF,WCYI,UAAA,OC2CF,yBFvDF,WCYI,UAAA,OC2CF,yBFvDF,WCYI,UAAA,OC2CF,0BFvDF,WCYI,UAAA,QDAJ,iBCZA,MAAA,KACA,cAAA,KACA,aAAA,KACA,aAAA,KACA,YAAA,KDkBA,KCJA,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,aAAA,MACA,YAAA,MDOA,YACE,aAAA,EACA,YAAA,EAFF,iBVyjBF,0BUnjBM,cAAA,EACA,aAAA,EGjCJ,KAAA,OAAA,QAAA,QAAA,QAAA,OAAA,OAAA,OAAA,OAAA,OAAA,OAAA,OAAA,ObylBF,UAEqJ,QAAvI,UAAmG,WAAY,WAAY,WAAhH,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UACtG,aAFqJ,QAAvI,UAAmG,WAAY,WAAY,WAAhH,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UACtG,aAFkJ,QAAvI,UAAmG,WAAY,WAAY,WAAhH,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UACnG,aAEqJ,QAAvI,UAAmG,WAAY,WAAY,WAAhH,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UAAW,UACtG,aa5lBI,SAAA,SACA,MAAA,KACA,cAAA,KACA,aAAA,KAmBE,KACE,wBAAA,EAAA,WAAA,EACA,kBAAA,EAAA,UAAA,EACA,UAAA,KAEF,UACE,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,UAAA,KAIA,OFFN,SAAA,EAAA,EAAA,UAAA,KAAA,EAAA,EAAA,UAIA,UAAA,UEFM,OFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,OFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,OFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,OFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,OFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,OFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,OFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,OFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,QFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,QFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,QFFN,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KAIA,UAAA,KEGI,aAAwB,eAAA,GAAA,MAAA,GAExB,YAAuB,eAAA,GAAA,MAAA,GAGrB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,SAAwB,eAAA,EAAA,MAAA,EAAxB,UAAwB,eAAA,GAAA,MAAA,GAAxB,UAAwB,eAAA,GAAA,MAAA,GAAxB,UAAwB,eAAA,GAAA,MAAA,GAMtB,UFTR,YAAA,UESQ,UFTR,YAAA,WESQ,UFTR,YAAA,IESQ,UFTR,YAAA,WESQ,UFTR,YAAA,WESQ,UFTR,YAAA,IESQ,UFTR,YAAA,WESQ,UFTR,YAAA,WESQ,UFTR,YAAA,IESQ,WFTR,YAAA,WESQ,WFTR,YAAA,WCWE,yBC9BE,QACE,wBAAA,EAAA,WAAA,EACA,kBAAA,EAAA,UAAA,EACA,UAAA,KAEF,aACE,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,UAAA,KAIA,UFFN,SAAA,EAAA,EAAA,UAAA,KAAA,EAAA,EAAA,UAIA,UAAA,UEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KAIA,UAAA,KEGI,gBAAwB,eAAA,GAAA,MAAA,GAExB,eAAuB,eAAA,GAAA,MAAA,GAGrB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAMtB,aFTR,YAAA,EESQ,aFTR,YAAA,UESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,cFTR,YAAA,WESQ,cFTR,YAAA,YCWE,yBC9BE,QACE,wBAAA,EAAA,WAAA,EACA,kBAAA,EAAA,UAAA,EACA,UAAA,KAEF,aACE,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,UAAA,KAIA,UFFN,SAAA,EAAA,EAAA,UAAA,KAAA,EAAA,EAAA,UAIA,UAAA,UEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KAIA,UAAA,KEGI,gBAAwB,eAAA,GAAA,MAAA,GAExB,eAAuB,eAAA,GAAA,MAAA,GAGrB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAMtB,aFTR,YAAA,EESQ,aFTR,YAAA,UESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,cFTR,YAAA,WESQ,cFTR,YAAA,YCWE,yBC9BE,QACE,wBAAA,EAAA,WAAA,EACA,kBAAA,EAAA,UAAA,EACA,UAAA,KAEF,aACE,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,UAAA,KAIA,UFFN,SAAA,EAAA,EAAA,UAAA,KAAA,EAAA,EAAA,UAIA,UAAA,UEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KAIA,UAAA,KEGI,gBAAwB,eAAA,GAAA,MAAA,GAExB,eAAuB,eAAA,GAAA,MAAA,GAGrB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAMtB,aFTR,YAAA,EESQ,aFTR,YAAA,UESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,cFTR,YAAA,WESQ,cFTR,YAAA,YCWE,0BC9BE,QACE,wBAAA,EAAA,WAAA,EACA,kBAAA,EAAA,UAAA,EACA,UAAA,KAEF,aACE,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,UAAA,KAIA,UFFN,SAAA,EAAA,EAAA,UAAA,KAAA,EAAA,EAAA,UAIA,UAAA,UEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,UFFN,SAAA,EAAA,EAAA,IAAA,KAAA,EAAA,EAAA,IAIA,UAAA,IEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,WAAA,KAAA,EAAA,EAAA,WAIA,UAAA,WEFM,WFFN,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KAIA,UAAA,KEGI,gBAAwB,eAAA,GAAA,MAAA,GAExB,eAAuB,eAAA,GAAA,MAAA,GAGrB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,YAAwB,eAAA,EAAA,MAAA,EAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAAxB,aAAwB,eAAA,GAAA,MAAA,GAMtB,aFTR,YAAA,EESQ,aFTR,YAAA,UESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,aFTR,YAAA,WESQ,aFTR,YAAA,WESQ,aFTR,YAAA,IESQ,cFTR,YAAA,WESQ,cFTR,YAAA,YG7CF,OACE,MAAA,KACA,cAAA,KACA,MAAA,Qdy+CF,Uc5+CA,UAQI,QAAA,OACA,eAAA,IACA,WAAA,IAAA,MAAA,QAVJ,gBAcI,eAAA,OACA,cAAA,IAAA,MAAA,QAfJ,mBAmBI,WAAA,IAAA,MAAA,Qdy+CJ,ach+CA,aAGI,QAAA,MASJ,gBACE,OAAA,IAAA,MAAA,Qd49CF,mBc79CA,mBAKI,OAAA,IAAA,MAAA,Qd69CJ,yBcl+CA,yBAWM,oBAAA,Id89CN,8BAFA,qBcv9CA,qBdw9CA,2Bcn9CI,OAAA,EAQJ,yCAEI,iBAAA,gBX/DF,4BW2EI,MAAA,QACA,iBAAA,iBCnFJ,ef+hDF,kBADA,kBe1hDM,iBAAA,QfkiDN,2BAFA,kBepiDE,kBfqiDF,wBezhDQ,aAAA,QZLN,kCYiBM,iBAAA,QALN,qCf4hDF,qCenhDU,iBAAA,QA5BR,iBfqjDF,oBADA,oBehjDM,iBAAA,QfwjDN,6BAFA,oBe1jDE,oBf2jDF,0Be/iDQ,aAAA,QZLN,oCYiBM,iBAAA,QALN,uCfkjDF,uCeziDU,iBAAA,QA5BR,ef2kDF,kBADA,kBetkDM,iBAAA,Qf8kDN,2BAFA,kBehlDE,kBfilDF,wBerkDQ,aAAA,QZLN,kCYiBM,iBAAA,QALN,qCfwkDF,qCe/jDU,iBAAA,QA5BR,YfimDF,eADA,ee5lDM,iBAAA,QfomDN,wBAFA,eetmDE,efumDF,qBe3lDQ,aAAA,QZLN,+BYiBM,iBAAA,QALN,kCf8lDF,kCerlDU,iBAAA,QA5BR,efunDF,kBADA,kBelnDM,iBAAA,Qf0nDN,2BAFA,kBe5nDE,kBf6nDF,wBejnDQ,aAAA,QZLN,kCYiBM,iBAAA,QALN,qCfonDF,qCe3mDU,iBAAA,QA5BR,cf6oDF,iBADA,iBexoDM,iBAAA,QfgpDN,0BAFA,iBelpDE,iBfmpDF,uBevoDQ,aAAA,QZLN,iCYiBM,iBAAA,QALN,oCf0oDF,oCejoDU,iBAAA,QA5BR,afmqDF,gBADA,gBe9pDM,iBAAA,QfsqDN,yBAFA,gBexqDE,gBfyqDF,sBe7pDQ,aAAA,QZLN,gCYiBM,iBAAA,QALN,mCfgqDF,mCevpDU,iBAAA,QA5BR,YfyrDF,eADA,eeprDM,iBAAA,Qf4rDN,wBAFA,ee9rDE,ef+rDF,qBenrDQ,aAAA,QZLN,+BYiBM,iBAAA,QALN,kCfsrDF,kCe7qDU,iBAAA,QA5BR,cf+sDF,iBADA,iBe1sDM,iBAAA,iBZGJ,iCYiBM,iBAAA,iBALN,oCfqsDF,oCe5rDU,iBAAA,iBD8EV,sBAGM,MAAA,KACA,iBAAA,QACA,aAAA,QALN,uBAWM,MAAA,QACA,iBAAA,QACA,aAAA,QAKN,YACE,MAAA,KACA,iBAAA,QdgnDF,eclnDA,edmnDA,qBc5mDI,aAAA,QAPJ,2BAWI,OAAA,EAXJ,oDAgBM,iBAAA,sBXrIJ,uCW4IM,MAAA,KACA,iBAAA,uBFhFJ,4BEiGA,qBAEI,QAAA,MACA,MAAA,KACA,WAAA,KACA,2BAAA,MALH,qCASK,OAAA,GF1GN,4BEiGA,qBAEI,QAAA,MACA,MAAA,KACA,WAAA,KACA,2BAAA,MALH,qCASK,OAAA,GF1GN,4BEiGA,qBAEI,QAAA,MACA,MAAA,KACA,WAAA,KACA,2BAAA,MALH,qCASK,OAAA,GF1GN,6BEiGA,qBAEI,QAAA,MACA,MAAA,KACA,WAAA,KACA,2BAAA,MALH,qCASK,OAAA,GAdV,kBAOQ,QAAA,MACA,MAAA,KACA,WAAA,KACA,2BAAA,MAVR,kCAcU,OAAA,EE7KV,cACE,QAAA,MACA,MAAA,KACA,OAAA,2BACA,QAAA,QAAA,OfqHI,UAAA,KelHJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,QRbE,cAAA,OSCE,WAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAKF,uCDLJ,cCMM,WAAA,MDNN,0BAsBI,iBAAA,YACA,OAAA,EEhBF,oBACE,MAAA,QACA,iBAAA,KACA,aAAA,QACA,QAAA,EAKE,WAAA,EAAA,EAAA,EAAA,MAAA,oBFhBN,yCA+BI,MAAA,QAEA,QAAA,EAjCJ,gCA+BI,MAAA,QAEA,QAAA,EAjCJ,oCA+BI,MAAA,QAEA,QAAA,EAjCJ,qCA+BI,MAAA,QAEA,QAAA,EAjCJ,2BA+BI,MAAA,QAEA,QAAA,EAjCJ,uBAAA,wBA2CI,iBAAA,QAEA,QAAA,EAIJ,qCAOI,MAAA,QACA,iBAAA,KAKJ,mBhBm0DA,oBgBj0DE,QAAA,MACA,MAAA,KAUF,gBACE,YAAA,oBACA,eAAA,oBACA,cAAA,EfZE,UAAA,QecF,YAAA,IAGF,mBACE,YAAA,kBACA,eAAA,kBfoCI,UAAA,QelCJ,YAAA,IAGF,mBACE,YAAA,mBACA,eAAA,mBf6BI,UAAA,Qe3BJ,YAAA,IASF,wBACE,QAAA,MACA,MAAA,KACA,YAAA,QACA,eAAA,QACA,cAAA,EACA,YAAA,IACA,MAAA,QACA,iBAAA,YACA,OAAA,MAAA,YACA,aAAA,IAAA,EAVF,wCAAA,wCAcI,cAAA,EACA,aAAA,EAYJ,iBACE,OAAA,0BACA,QAAA,OAAA,MfXI,UAAA,QeaJ,YAAA,IRvIE,cAAA,MQ2IJ,iBACE,OAAA,yBACA,QAAA,MAAA,KfnBI,UAAA,QeqBJ,YAAA,IR/IE,cAAA,MQoJJ,8BAAA,0BAGI,OAAA,KAIJ,sBACE,OAAA,KAQF,YACE,cAAA,KAGF,WACE,QAAA,MACA,WAAA,OAQF,UACE,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,aAAA,KACA,YAAA,KAJF,ehBwyDA,wBgBhyDI,cAAA,IACA,aAAA,IASJ,YACE,SAAA,SACA,QAAA,MACA,aAAA,QAGF,kBACE,SAAA,SACA,WAAA,MACA,YAAA,SAHF,6CAMI,MAAA,QAIJ,kBACE,cAAA,EAGF,mBACE,QAAA,mBAAA,QAAA,YACA,eAAA,OAAA,YAAA,OACA,aAAA,EACA,aAAA,OAJF,qCAQI,SAAA,OACA,WAAA,EACA,aAAA,SACA,YAAA,EE3MF,gBACE,QAAA,KACA,MAAA,KACA,WAAA,OjBwCA,UAAA,IiBtCA,MAAA,QAGF,eACE,SAAA,SACA,IAAA,KACA,QAAA,EACA,QAAA,KACA,UAAA,KACA,QAAA,OAAA,MACA,WAAA,MjBmFE,UAAA,QiBjFF,YAAA,IACA,MAAA,KACA,iBAAA,mBV3CA,cAAA,OUgDA,uBAAA,mCAEE,aAAA,QAGE,cAAA,qBACA,iBAAA,2OACA,kBAAA,UACA,oBAAA,OAAA,MAAA,wBACA,gBAAA,sBAAA,sBATJ,6BAAA,yCAaI,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBlB2+D6C,uCACrD,sCkB1/DI,mDlBy/DJ,kDkBt+DQ,QAAA,MAOJ,2CAAA,+BAGI,cAAA,qBACA,oBAAA,IAAA,wBAAA,MAAA,wBAMJ,wBAAA,oCAEE,aAAA,QAGE,cAAA,uCACA,WAAA,0JAAA,UAAA,MAAA,OAAA,MAAA,CAAA,IAAA,IAAA,CAAA,2OAAA,KAAA,UAAA,OAAA,MAAA,OAAA,CAAA,sBAAA,sBANJ,8BAAA,0CAUI,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBlBg+D8C,wCACtD,uCkB5+DI,oDlB2+DJ,mDkB39DQ,QAAA,MlBi+DkD,4CAC1D,2CkB39DI,wDlB09DJ,uDkBt9DQ,QAAA,MAMJ,6CAAA,yDAGI,MAAA,QlBu9DiD,2CACzD,0CkB39DI,uDlB09DJ,sDkBl9DQ,QAAA,MAMJ,qDAAA,iEAGI,MAAA,QAHJ,6DAAA,yEAMM,aAAA,QlBo9DmD,+CAC7D,8CkB39DI,2DlB09DJ,0DkB98DQ,QAAA,MAZJ,qEAAA,iFAiBM,aAAA,QCnJN,iBAAA,QDkIA,mEAAA,+EAwBM,WAAA,EAAA,EAAA,EAAA,MAAA,oBAxBN,iFAAA,6FA4BM,aAAA,QAQN,+CAAA,2DAGI,aAAA,QlB08DkD,4CAC1D,2CkB98DI,wDlB68DJ,uDkBr8DQ,QAAA,MARJ,qDAAA,iEAaM,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBA7JR,kBACE,QAAA,KACA,MAAA,KACA,WAAA,OjBwCA,UAAA,IiBtCA,MAAA,QAGF,iBACE,SAAA,SACA,IAAA,KACA,QAAA,EACA,QAAA,KACA,UAAA,KACA,QAAA,OAAA,MACA,WAAA,MjBmFE,UAAA,QiBjFF,YAAA,IACA,MAAA,KACA,iBAAA,mBV3CA,cAAA,OUgDA,yBAAA,qCAEE,aAAA,QAGE,cAAA,qBACA,iBAAA,qRACA,kBAAA,UACA,oBAAA,OAAA,MAAA,wBACA,gBAAA,sBAAA,sBATJ,+BAAA,2CAaI,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBlBsmEiD,2CACzD,0CkBrnEI,uDlBonEJ,sDkBjmEQ,QAAA,MAOJ,6CAAA,iCAGI,cAAA,qBACA,oBAAA,IAAA,wBAAA,MAAA,wBAMJ,0BAAA,sCAEE,aAAA,QAGE,cAAA,uCACA,WAAA,0JAAA,UAAA,MAAA,OAAA,MAAA,CAAA,IAAA,IAAA,CAAA,qRAAA,KAAA,UAAA,OAAA,MAAA,OAAA,CAAA,sBAAA,sBANJ,gCAAA,4CAUI,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBlB2lEkD,4CAC1D,2CkBvmEI,wDlBsmEJ,uDkBtlEQ,QAAA,MlB4lEsD,gDAC9D,+CkBtlEI,4DlBqlEJ,2DkBjlEQ,QAAA,MAMJ,+CAAA,2DAGI,MAAA,QlBklEqD,+CAC7D,8CkBtlEI,2DlBqlEJ,0DkB7kEQ,QAAA,MAMJ,uDAAA,mEAGI,MAAA,QAHJ,+DAAA,2EAMM,aAAA,QlB+kEuD,mDACjE,kDkBtlEI,+DlBqlEJ,8DkBzkEQ,QAAA,MAZJ,uEAAA,mFAiBM,aAAA,QCnJN,iBAAA,QDkIA,qEAAA,iFAwBM,WAAA,EAAA,EAAA,EAAA,MAAA,oBAxBN,mFAAA,+FA4BM,aAAA,QAQN,iDAAA,6DAGI,aAAA,QlBqkEsD,gDAC9D,+CkBzkEI,4DlBwkEJ,2DkBhkEQ,QAAA,MARJ,uDAAA,mEAaM,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBFuEV,aACE,QAAA,YAAA,QAAA,KACA,cAAA,IAAA,KAAA,UAAA,IAAA,KACA,eAAA,OAAA,YAAA,OAHF,yBASI,MAAA,KJ9MA,yBIqMJ,mBAeM,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OACA,cAAA,OAAA,gBAAA,OACA,cAAA,EAlBN,yBAuBM,QAAA,YAAA,QAAA,KACA,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,cAAA,IAAA,KAAA,UAAA,IAAA,KACA,eAAA,OAAA,YAAA,OACA,cAAA,EA3BN,2BAgCM,QAAA,aACA,MAAA,KACA,eAAA,OAlCN,qCAuCM,QAAA,ahBigEJ,4BgBxiEF,0BA4CM,MAAA,KA5CN,yBAkDM,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OACA,cAAA,OAAA,gBAAA,OACA,MAAA,KACA,aAAA,EAtDN,+BAyDM,SAAA,SACA,kBAAA,EAAA,YAAA,EACA,WAAA,EACA,aAAA,OACA,YAAA,EA7DN,6BAiEM,eAAA,OAAA,YAAA,OACA,cAAA,OAAA,gBAAA,OAlEN,mCAqEM,cAAA,GIhUN,KACE,QAAA,aAEA,YAAA,IACA,MAAA,QACA,WAAA,OACA,eAAA,OACA,oBAAA,KAAA,iBAAA,KAAA,gBAAA,KAAA,YAAA,KACA,iBAAA,YACA,OAAA,IAAA,MAAA,YCsFA,QAAA,QAAA,OpB0BI,UAAA,KoBxBJ,YAAA,IblGE,cAAA,OSCE,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAKF,uCGLJ,KHMM,WAAA,MdAJ,WiBQE,MAAA,QACA,gBAAA,KAfJ,WAAA,WAoBI,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBArBJ,cAAA,cA2BI,QAAA,IAeJ,epBi0EA,wBoB/zEE,eAAA,KASA,aCrDA,MAAA,KFAE,iBAAA,QEEF,aAAA,QlBIA,mBkBAE,MAAA,KFNA,iBAAA,QEQA,aAAA,QAGF,mBAAA,mBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,oBAKJ,sBAAA,sBAEE,MAAA,KACA,iBAAA,QACA,aAAA,QAOF,kDAAA,kDrBq2EF,mCqBl2EI,MAAA,KACA,iBAAA,QAIA,aAAA,QAEA,wDAAA,wDrBk2EJ,yCqB71EQ,WAAA,EAAA,EAAA,EAAA,MAAA,oBDKN,eCrDA,MAAA,KFAE,iBAAA,QEEF,aAAA,QlBIA,qBkBAE,MAAA,KFNA,iBAAA,QEQA,aAAA,QAGF,qBAAA,qBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,qBAKJ,wBAAA,wBAEE,MAAA,KACA,iBAAA,QACA,aAAA,QAOF,oDAAA,oDrBu4EF,qCqBp4EI,MAAA,KACA,iBAAA,QAIA,aAAA,QAEA,0DAAA,0DrBo4EJ,2CqB/3EQ,WAAA,EAAA,EAAA,EAAA,MAAA,qBDKN,aCrDA,MAAA,KFAE,iBAAA,QEEF,aAAA,QlBIA,mBkBAE,MAAA,KFNA,iBAAA,QEQA,aAAA,QAGF,mBAAA,mBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,mBAKJ,sBAAA,sBAEE,MAAA,KACA,iBAAA,QACA,aAAA,QAOF,kDAAA,kDrBy6EF,mCqBt6EI,MAAA,KACA,iBAAA,QAIA,aAAA,QAEA,wDAAA,wDrBs6EJ,yCqBj6EQ,WAAA,EAAA,EAAA,EAAA,MAAA,mBDKN,UCrDA,MAAA,KFAE,iBAAA,QEEF,aAAA,QlBIA,gBkBAE,MAAA,KFNA,iBAAA,QEQA,aAAA,QAGF,gBAAA,gBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,oBAKJ,mBAAA,mBAEE,MAAA,KACA,iBAAA,QACA,aAAA,QAOF,+CAAA,+CrB28EF,gCqBx8EI,MAAA,KACA,iBAAA,QAIA,aAAA,QAEA,qDAAA,qDrBw8EJ,sCqBn8EQ,WAAA,EAAA,EAAA,EAAA,MAAA,oBDKN,aCrDA,MAAA,QFAE,iBAAA,QEEF,aAAA,QlBIA,mBkBAE,MAAA,QFNA,iBAAA,QEQA,aAAA,QAGF,mBAAA,mBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,oBAKJ,sBAAA,sBAEE,MAAA,QACA,iBAAA,QACA,aAAA,QAOF,kDAAA,kDrB6+EF,mCqB1+EI,MAAA,QACA,iBAAA,QAIA,aAAA,QAEA,wDAAA,wDrB0+EJ,yCqBr+EQ,WAAA,EAAA,EAAA,EAAA,MAAA,oBDKN,YCrDA,MAAA,KFAE,iBAAA,QEEF,aAAA,QlBIA,kBkBAE,MAAA,KFNA,iBAAA,QEQA,aAAA,QAGF,kBAAA,kBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,mBAKJ,qBAAA,qBAEE,MAAA,KACA,iBAAA,QACA,aAAA,QAOF,iDAAA,iDrB+gFF,kCqB5gFI,MAAA,KACA,iBAAA,QAIA,aAAA,QAEA,uDAAA,uDrB4gFJ,wCqBvgFQ,WAAA,EAAA,EAAA,EAAA,MAAA,mBDKN,WCrDA,MAAA,QFAE,iBAAA,QEEF,aAAA,QlBIA,iBkBAE,MAAA,QFNA,iBAAA,QEQA,aAAA,QAGF,iBAAA,iBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,qBAKJ,oBAAA,oBAEE,MAAA,QACA,iBAAA,QACA,aAAA,QAOF,gDAAA,gDrBijFF,iCqB9iFI,MAAA,QACA,iBAAA,QAIA,aAAA,QAEA,sDAAA,sDrB8iFJ,uCqBziFQ,WAAA,EAAA,EAAA,EAAA,MAAA,qBDKN,UCrDA,MAAA,KFAE,iBAAA,QEEF,aAAA,QlBIA,gBkBAE,MAAA,KFNA,iBAAA,QEQA,aAAA,QAGF,gBAAA,gBAMI,WAAA,EAAA,EAAA,EAAA,MAAA,kBAKJ,mBAAA,mBAEE,MAAA,KACA,iBAAA,QACA,aAAA,QAOF,+CAAA,+CrBmlFF,gCqBhlFI,MAAA,KACA,iBAAA,QAIA,aAAA,QAEA,qDAAA,qDrBglFJ,sCqB3kFQ,WAAA,EAAA,EAAA,EAAA,MAAA,kBDWN,qBCJA,MAAA,QACA,aAAA,QlBlDA,2BkBqDE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,2BAAA,2BAEE,WAAA,EAAA,EAAA,EAAA,MAAA,mBAGF,8BAAA,8BAEE,MAAA,QACA,iBAAA,YAGF,0DAAA,0DrBykFF,2CqBtkFI,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,gEAAA,gErBykFJ,iDqBpkFQ,WAAA,EAAA,EAAA,EAAA,MAAA,mBD5BN,uBCJA,MAAA,QACA,aAAA,QlBlDA,6BkBqDE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,6BAAA,6BAEE,WAAA,EAAA,EAAA,EAAA,MAAA,qBAGF,gCAAA,gCAEE,MAAA,QACA,iBAAA,YAGF,4DAAA,4DrBymFF,6CqBtmFI,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,kEAAA,kErBymFJ,mDqBpmFQ,WAAA,EAAA,EAAA,EAAA,MAAA,qBD5BN,qBCJA,MAAA,QACA,aAAA,QlBlDA,2BkBqDE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,2BAAA,2BAEE,WAAA,EAAA,EAAA,EAAA,MAAA,mBAGF,8BAAA,8BAEE,MAAA,QACA,iBAAA,YAGF,0DAAA,0DrByoFF,2CqBtoFI,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,gEAAA,gErByoFJ,iDqBpoFQ,WAAA,EAAA,EAAA,EAAA,MAAA,mBD5BN,kBCJA,MAAA,QACA,aAAA,QlBlDA,wBkBqDE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,wBAAA,wBAEE,WAAA,EAAA,EAAA,EAAA,MAAA,oBAGF,2BAAA,2BAEE,MAAA,QACA,iBAAA,YAGF,uDAAA,uDrByqFF,wCqBtqFI,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,6DAAA,6DrByqFJ,8CqBpqFQ,WAAA,EAAA,EAAA,EAAA,MAAA,oBD5BN,qBCJA,MAAA,QACA,aAAA,QlBlDA,2BkBqDE,MAAA,QACA,iBAAA,QACA,aAAA,QAGF,2BAAA,2BAEE,WAAA,EAAA,EAAA,EAAA,MAAA,mBAGF,8BAAA,8BAEE,MAAA,QACA,iBAAA,YAGF,0DAAA,0DrBysFF,2CqBtsFI,MAAA,QACA,iBAAA,QACA,aAAA,QAEA,gEAAA,gErBysFJ,iDqBpsFQ,WAAA,EAAA,EAAA,EAAA,MAAA,mBD5BN,oBCJA,MAAA,QACA,aAAA,QlBlDA,0BkBqDE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,0BAAA,0BAEE,WAAA,EAAA,EAAA,EAAA,MAAA,mBAGF,6BAAA,6BAEE,MAAA,QACA,iBAAA,YAGF,yDAAA,yDrByuFF,0CqBtuFI,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,+DAAA,+DrByuFJ,gDqBpuFQ,WAAA,EAAA,EAAA,EAAA,MAAA,mBD5BN,mBCJA,MAAA,QACA,aAAA,QlBlDA,yBkBqDE,MAAA,QACA,iBAAA,QACA,aAAA,QAGF,yBAAA,yBAEE,WAAA,EAAA,EAAA,EAAA,MAAA,qBAGF,4BAAA,4BAEE,MAAA,QACA,iBAAA,YAGF,wDAAA,wDrBywFF,yCqBtwFI,MAAA,QACA,iBAAA,QACA,aAAA,QAEA,8DAAA,8DrBywFJ,+CqBpwFQ,WAAA,EAAA,EAAA,EAAA,MAAA,qBD5BN,kBCJA,MAAA,QACA,aAAA,QlBlDA,wBkBqDE,MAAA,KACA,iBAAA,QACA,aAAA,QAGF,wBAAA,wBAEE,WAAA,EAAA,EAAA,EAAA,MAAA,kBAGF,2BAAA,2BAEE,MAAA,QACA,iBAAA,YAGF,uDAAA,uDrByyFF,wCqBtyFI,MAAA,KACA,iBAAA,QACA,aAAA,QAEA,6DAAA,6DrByyFJ,8CqBpyFQ,WAAA,EAAA,EAAA,EAAA,MAAA,kBDjBR,UACE,YAAA,IACA,MAAA,QACA,gBAAA,KjBnEA,gBiBsEE,MAAA,QACA,gBAAA,UAPJ,gBAAA,gBAYI,gBAAA,UACA,WAAA,KAbJ,mBAAA,mBAkBI,MAAA,QACA,eAAA,KAWJ,mBAAA,QCLE,QAAA,MAAA,KpB0BI,UAAA,QoBxBJ,YAAA,IblGE,cAAA,MYyGJ,mBAAA,QCTE,QAAA,OAAA,MpB0BI,UAAA,QoBxBJ,YAAA,IblGE,cAAA,MYkHJ,WACE,QAAA,MACA,MAAA,KAFF,sBAMI,WAAA,MpBszFJ,6BADA,4BoBhzFA,6BAII,MAAA,KEtIJ,MLMM,WAAA,QAAA,KAAA,OAKF,uCKXJ,MLYM,WAAA,MKZN,iBAII,QAAA,EAIJ,qBAEI,QAAA,KAIJ,YACE,SAAA,SACA,OAAA,EACA,SAAA,OLXI,WAAA,OAAA,KAAA,KAKF,uCKGJ,YLFM,WAAA,MjB48FN,UACA,UAFA,WuBt9FA,QAIE,SAAA,SAGF,iBACE,YAAA,OCoBE,wBACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAhCJ,WAAA,KAAA,MACA,aAAA,KAAA,MAAA,YACA,cAAA,EACA,YAAA,KAAA,MAAA,YAqDE,8BACE,YAAA,ED1CN,eACE,SAAA,SACA,IAAA,KACA,KAAA,EACA,QAAA,KACA,QAAA,KACA,MAAA,KACA,UAAA,MACA,QAAA,MAAA,EACA,OAAA,QAAA,EAAA,EtBsGI,UAAA,KsBpGJ,MAAA,QACA,WAAA,KACA,WAAA,KACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,gBf3BE,cAAA,OeoCA,oBACE,MAAA,KACA,KAAA,EAGF,qBACE,MAAA,EACA,KAAA,KXYF,yBWnBA,uBACE,MAAA,KACA,KAAA,EAGF,wBACE,MAAA,EACA,KAAA,MXYF,yBWnBA,uBACE,MAAA,KACA,KAAA,EAGF,wBACE,MAAA,EACA,KAAA,MXYF,yBWnBA,uBACE,MAAA,KACA,KAAA,EAGF,wBACE,MAAA,EACA,KAAA,MXYF,0BWnBA,uBACE,MAAA,KACA,KAAA,EAGF,wBACE,MAAA,EACA,KAAA,MAON,uBAEI,IAAA,KACA,OAAA,KACA,WAAA,EACA,cAAA,QC/BA,gCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAzBJ,WAAA,EACA,aAAA,KAAA,MAAA,YACA,cAAA,KAAA,MACA,YAAA,KAAA,MAAA,YA8CE,sCACE,YAAA,EDUN,0BAEI,IAAA,EACA,MAAA,KACA,KAAA,KACA,WAAA,EACA,YAAA,QC7CA,mCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAlBJ,WAAA,KAAA,MAAA,YACA,aAAA,EACA,cAAA,KAAA,MAAA,YACA,YAAA,KAAA,MAuCE,yCACE,YAAA,EA7BF,mCDmDE,eAAA,EAKN,yBAEI,IAAA,EACA,MAAA,KACA,KAAA,KACA,WAAA,EACA,aAAA,QC9DA,kCACE,QAAA,aACA,YAAA,OACA,eAAA,OACA,QAAA,GAJF,kCAgBI,QAAA,KAGF,mCACE,QAAA,aACA,aAAA,OACA,eAAA,OACA,QAAA,GA9BN,WAAA,KAAA,MAAA,YACA,aAAA,KAAA,MACA,cAAA,KAAA,MAAA,YAiCE,wCACE,YAAA,EAVA,mCDiDA,eAAA,EAON,oCAAA,kCAAA,mCAAA,iCAKI,MAAA,KACA,OAAA,KAKJ,kBE9GE,OAAA,EACA,OAAA,MAAA,EACA,SAAA,OACA,WAAA,IAAA,MAAA,QFkHF,eACE,QAAA,MACA,MAAA,KACA,QAAA,OAAA,OACA,MAAA,KACA,YAAA,IACA,MAAA,QACA,WAAA,QACA,YAAA,OACA,iBAAA,YACA,OAAA,EpBpHA,qBAAA,qBoBmIE,MAAA,QACA,gBAAA,KJ9IA,iBAAA,QIoHJ,sBAAA,sBAgCI,MAAA,KACA,gBAAA,KJrJA,iBAAA,QIoHJ,wBAAA,wBAuCI,MAAA,QACA,eAAA,KACA,iBAAA,YAQJ,oBACE,QAAA,MAIF,iBACE,QAAA,MACA,QAAA,MAAA,OACA,cAAA,EtBpDI,UAAA,QsBsDJ,MAAA,QACA,YAAA,OAIF,oBACE,QAAA,MACA,QAAA,OAAA,OACA,MAAA,QG1LF,W1B4sGA,oB0B1sGE,SAAA,SACA,QAAA,mBAAA,QAAA,YACA,eAAA,O1BgtGF,yB0BptGA,gBAOI,SAAA,SACA,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,K1BmtGJ,+BGltGE,sBuBII,QAAA,E1BqtGN,gCADA,gCADA,+B0BhuGA,uBAAA,uBAAA,sBAkBM,QAAA,EAMN,aACE,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,cAAA,MAAA,gBAAA,WAHF,0BAMI,MAAA,K1BstGJ,wC0BltGA,kCAII,YAAA,K1BmtGJ,4C0BvtGA,uDlBhBI,wBAAA,EACA,2BAAA,ER4uGJ,6C0B7tGA,kClBFI,uBAAA,EACA,0BAAA,EkBgCJ,uBACE,cAAA,SACA,aAAA,SAFF,8B1B0sGA,yCADA,sC0BlsGI,YAAA,EAGF,yCACE,aAAA,EAIJ,0CAAA,+BACE,cAAA,QACA,aAAA,QAGF,0CAAA,+BACE,cAAA,OACA,aAAA,OAoBF,oBACE,mBAAA,OAAA,eAAA,OACA,eAAA,MAAA,YAAA,WACA,cAAA,OAAA,gBAAA,OAHF,yB1B4rGA,+B0BrrGI,MAAA,K1B0rGJ,iD0BjsGA,2CAYI,WAAA,K1B0rGJ,qD0BtsGA,gElBlFI,2BAAA,EACA,0BAAA,ER6xGJ,sD0B5sGA,2ClBhGI,uBAAA,EACA,wBAAA,EkBuIJ,uB1B0qGA,kC0BvqGI,cAAA,E1B4qGJ,4C0B/qGA,yC1BirGA,uDADA,oD0BzqGM,SAAA,SACA,KAAA,cACA,eAAA,KCzJN,aACE,SAAA,SACA,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,eAAA,QAAA,YAAA,QACA,MAAA,K3Bg1GF,0BADA,4B2Bp1GA,2B3Bm1GA,qC2Bx0GI,SAAA,SACA,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KAGA,MAAA,GACA,cAAA,E3Bw1GJ,uCADA,yCADA,wCADA,yCADA,2CADA,0CAJA,wCADA,0C2B91GA,yC3Bk2GA,kDADA,oDADA,mD2B30GM,YAAA,K3By1GN,sEADA,kC2B72GA,iCA6BI,QAAA,EA7BJ,mDAkCI,QAAA,E3Bq1GJ,6C2Bv3GA,4CnBeI,wBAAA,EACA,2BAAA,ER62GJ,8C2B73GA,6CnB6BI,uBAAA,EACA,0BAAA,EmB9BJ,0BA8CI,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OA/CJ,8D3B04GA,qEQ33GI,wBAAA,EACA,2BAAA,EmBhBJ,+DnB6BI,uBAAA,EACA,0BAAA,ERu3GJ,oB2Bv1GA,qBAEE,QAAA,YAAA,QAAA,K3B21GF,yB2B71GA,0BAQI,SAAA,SACA,QAAA,E3B01GJ,+B2Bn2GA,gCAYM,QAAA,E3B+1GN,8BACA,2CAEA,2CADA,wD2B72GA,+B3Bw2GA,4CAEA,4CADA,yD2Br1GI,YAAA,KAIJ,qBAAuB,aAAA,KACvB,oBAAsB,YAAA,KAQtB,kBACE,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OACA,QAAA,QAAA,OACA,cAAA,E1BsBI,UAAA,K0BpBJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,OACA,YAAA,OACA,iBAAA,QACA,OAAA,IAAA,MAAA,QnB5GE,cAAA,OR48GJ,uC2B52GA,oCAkBI,WAAA,E3B+1GJ,+B2Br1GA,4CAEE,OAAA,yB3Bw1GF,+B2Br1GA,8B3By1GA,yCAFA,sDACA,0CAFA,uD2Bh1GE,QAAA,MAAA,K1BbI,UAAA,Q0BeJ,YAAA,InBzIE,cAAA,MRk+GJ,+B2Br1GA,4CAEE,OAAA,0B3Bw1GF,+B2Br1GA,8B3By1GA,yCAFA,sDACA,0CAFA,uD2Bh1GE,QAAA,OAAA,M1B9BI,UAAA,Q0BgCJ,YAAA,InB1JE,cAAA,MmB8JJ,+B3Bq1GA,+B2Bn1GE,cAAA,Q3B21GF,wFACA,+EAHA,uDACA,oE2B/0GA,uC3B60GA,oDQx+GI,wBAAA,EACA,2BAAA,EmBmKJ,sC3B80GA,mDAGA,qEACA,kFAHA,yDACA,sEQt+GI,uBAAA,EACA,0BAAA,EoB3BJ,gBACE,SAAA,SACA,QAAA,MACA,WAAA,OACA,aAAA,OAGF,uBACE,QAAA,mBAAA,QAAA,YACA,aAAA,KAGF,sBACE,SAAA,SACA,QAAA,GACA,QAAA,EAHF,4DAMI,MAAA,KACA,aAAA,QTtBA,iBAAA,QSeJ,0DAiBM,WAAA,EAAA,EAAA,EAAA,MAAA,oBAjBN,wEAsBI,aAAA,QAtBJ,0EA0BI,MAAA,KACA,iBAAA,QACA,aAAA,QA5BJ,qDAkCM,MAAA,QAlCN,6DAqCQ,iBAAA,QAUR,sBACE,SAAA,SACA,cAAA,EACA,eAAA,IAHF,8BAOI,SAAA,SACA,IAAA,OACA,KAAA,QACA,QAAA,MACA,MAAA,KACA,OAAA,KACA,eAAA,KACA,QAAA,GACA,iBAAA,KACA,OAAA,QAAA,MAAA,IAhBJ,6BAsBI,SAAA,SACA,IAAA,OACA,KAAA,QACA,QAAA,MACA,MAAA,KACA,OAAA,KACA,QAAA,GACA,WAAA,UAAA,GAAA,CAAA,IAAA,IASJ,+CpBrGI,cAAA,OoBqGJ,4EAOM,iBAAA,4LAPN,mFAaM,aAAA,QTjHF,iBAAA,QSoGJ,kFAkBM,iBAAA,yIAlBN,sFAwBM,iBAAA,mBAxBN,4FA2BM,iBAAA,mBASN,4CAGI,cAAA,IAHJ,yEAQM,iBAAA,sIARN,mFAcM,iBAAA,mBAUN,eACE,aAAA,QADF,6CAKM,KAAA,SACA,MAAA,QACA,eAAA,IAEA,cAAA,MATN,4CAaM,IAAA,mBACA,KAAA,qBACA,MAAA,iBACA,OAAA,iBACA,iBAAA,QAEA,cAAA,MXnLA,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,WAAA,CAAA,kBAAA,KAAA,YAAA,WAAA,UAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAAA,WAAA,UAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,WAAA,CAAA,kBAAA,KAAA,YAKF,uCW2JJ,4CX1JM,WAAA,MW0JN,0EA0BM,iBAAA,KACA,kBAAA,mBAAA,UAAA,mBA3BN,oFAiCM,iBAAA,mBAYN,eACE,QAAA,aACA,MAAA,KACA,OAAA,2BACA,QAAA,QAAA,QAAA,QAAA,O3BxFI,UAAA,K2B2FJ,YAAA,IACA,YAAA,IACA,MAAA,QACA,eAAA,OACA,WAAA,0JAAA,UAAA,MAAA,OAAA,MAAA,CAAA,IAAA,KACA,iBAAA,KACA,OAAA,IAAA,MAAA,QpB3NE,cAAA,OoB8NF,mBAAA,KAAA,gBAAA,KAAA,WAAA,KAhBF,qBAmBI,aAAA,QACA,QAAA,EAIE,WAAA,EAAA,EAAA,EAAA,MAAA,oBAxBN,gCAiCM,MAAA,QACA,iBAAA,KAlCN,yBAAA,qCAwCI,OAAA,KACA,cAAA,OACA,iBAAA,KA1CJ,wBA8CI,MAAA,QACA,iBAAA,QA/CJ,2BAoDI,QAAA,KAIJ,kBACE,OAAA,0BACA,YAAA,OACA,eAAA,OACA,aAAA,M3BhJI,UAAA,Q2BoJN,kBACE,OAAA,yBACA,YAAA,MACA,eAAA,MACA,aAAA,K3BxJI,UAAA,Q2BiKN,aACE,SAAA,SACA,QAAA,aACA,MAAA,KACA,OAAA,2BACA,cAAA,EAGF,mBACE,SAAA,SACA,QAAA,EACA,MAAA,KACA,OAAA,2BACA,OAAA,EACA,QAAA,EANF,4CASI,aAAA,QACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBAVJ,+CAcI,iBAAA,QAdJ,sDAmBM,QAAA,SAnBN,0DAwBI,QAAA,kBAIJ,mBACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,KAAA,EACA,QAAA,EACA,OAAA,2BACA,QAAA,QAAA,OAEA,YAAA,IACA,YAAA,IACA,MAAA,QACA,iBAAA,KACA,OAAA,IAAA,MAAA,QpB5UE,cAAA,OoB+TJ,0BAkBI,SAAA,SACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,QAAA,EACA,QAAA,MACA,OAAA,qBACA,QAAA,QAAA,OACA,YAAA,IACA,MAAA,QACA,QAAA,ST1VA,iBAAA,QS4VA,YAAA,QpB7VA,cAAA,EAAA,OAAA,OAAA,EoBwWJ,cACE,MAAA,KACA,OAAA,mBACA,QAAA,EACA,iBAAA,YACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KALF,oBAQI,QAAA,EARJ,0CAY8B,WAAA,EAAA,EAAA,EAAA,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,MAAA,oBAZ9B,sCAa8B,WAAA,EAAA,EAAA,EAAA,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,MAAA,oBAb9B,+BAc8B,WAAA,EAAA,EAAA,EAAA,IAAA,IAAA,CAAA,EAAA,EAAA,EAAA,MAAA,oBAd9B,gCAkBI,OAAA,EAlBJ,oCAsBI,MAAA,KACA,OAAA,KACA,WAAA,QT/XA,iBAAA,QSiYA,OAAA,EpBlYA,cAAA,KSCE,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YWqYF,mBAAA,KAAA,WAAA,KXhYA,uCWkWJ,oCXjWM,WAAA,MWiWN,2CTvWI,iBAAA,QSuWJ,6CAsCI,MAAA,KACA,OAAA,MACA,MAAA,YACA,OAAA,QACA,iBAAA,QACA,aAAA,YpBnZA,cAAA,KoBwWJ,gCAiDI,MAAA,KACA,OAAA,KTzZA,iBAAA,QS2ZA,OAAA,EpB5ZA,cAAA,KSCE,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YW+ZF,gBAAA,KAAA,WAAA,KX1ZA,uCWkWJ,gCXjWM,WAAA,MWiWN,uCTvWI,iBAAA,QSuWJ,gCAgEI,MAAA,KACA,OAAA,MACA,MAAA,YACA,OAAA,QACA,iBAAA,QACA,aAAA,YpB7aA,cAAA,KoBwWJ,yBA2EI,MAAA,KACA,OAAA,KACA,WAAA,EACA,aAAA,MACA,YAAA,MTtbA,iBAAA,QSwbA,OAAA,EpBzbA,cAAA,KSCE,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YW4bF,WAAA,KXvbA,uCWkWJ,yBXjWM,WAAA,MWiWN,gCTvWI,iBAAA,QSuWJ,yBA6FI,MAAA,KACA,OAAA,MACA,MAAA,YACA,OAAA,QACA,iBAAA,YACA,aAAA,YACA,aAAA,MAnGJ,8BAwGI,iBAAA,QpBhdA,cAAA,KoBwWJ,8BA6GI,aAAA,KACA,iBAAA,QpBtdA,cAAA,KoBwWJ,6CAoHM,iBAAA,QApHN,sDAwHM,OAAA,QAxHN,yCA4HM,iBAAA,QA5HN,yCAgIM,OAAA,QAhIN,kCAoIM,iBAAA,QAKN,8B5Bi9GA,mBACA,eiBl8HM,WAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAKF,uCW2eJ,8B5Bw9GE,mBACA,eiBn8HI,WAAA,MYPN,KACE,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,aAAA,EACA,cAAA,EACA,WAAA,KAGF,UACE,QAAA,MACA,QAAA,MAAA,K1BCA,gBAAA,gB0BEE,gBAAA,KALJ,mBAUI,MAAA,QACA,eAAA,KACA,OAAA,QAQJ,UACE,cAAA,IAAA,MAAA,QADF,oBAII,cAAA,KAJJ,oBAQI,OAAA,IAAA,MAAA,YrB3BA,uBAAA,OACA,wBAAA,OLCF,0BAAA,0B0B6BI,aAAA,QAAA,QAAA,QAZN,6BAgBM,MAAA,QACA,iBAAA,YACA,aAAA,Y7Bm9HN,mC6Br+HA,2BAwBI,MAAA,QACA,iBAAA,KACA,aAAA,QAAA,QAAA,KA1BJ,yBA+BI,WAAA,KrBlDA,uBAAA,EACA,wBAAA,EqB4DJ,qBrBtEI,cAAA,OqBsEJ,4B7B48HA,2B6Br8HI,MAAA,KACA,iBAAA,QASJ,oBAEI,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,WAAA,OAIJ,yBAEI,wBAAA,EAAA,WAAA,EACA,kBAAA,EAAA,UAAA,EACA,WAAA,OASJ,uBAEI,QAAA,KAFJ,qBAKI,QAAA,MCpGJ,QACE,SAAA,SACA,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,eAAA,OAAA,YAAA,OACA,cAAA,QAAA,gBAAA,cACA,QAAA,MAAA,KANF,mB9B+iIA,yB8BniII,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,eAAA,OAAA,YAAA,OACA,cAAA,QAAA,gBAAA,cASJ,cACE,QAAA,aACA,YAAA,SACA,eAAA,SACA,aAAA,K7BkFI,UAAA,Q6BhFJ,YAAA,QACA,YAAA,O3BhCA,oBAAA,oB2BmCE,gBAAA,KASJ,YACE,QAAA,YAAA,QAAA,KACA,mBAAA,OAAA,eAAA,OACA,aAAA,EACA,cAAA,EACA,WAAA,KALF,sBAQI,cAAA,EACA,aAAA,EATJ,2BAaI,SAAA,OACA,MAAA,KASJ,aACE,QAAA,aACA,YAAA,MACA,eAAA,MAYF,iBACE,wBAAA,KAAA,WAAA,KACA,kBAAA,EAAA,UAAA,EAGA,eAAA,OAAA,YAAA,OAIF,gBACE,QAAA,OAAA,O7BmBI,UAAA,Q6BjBJ,YAAA,EACA,iBAAA,YACA,OAAA,IAAA,MAAA,YtB3GE,cAAA,OLWF,sBAAA,sB2BoGE,gBAAA,KAMJ,qBACE,QAAA,aACA,MAAA,MACA,OAAA,MACA,eAAA,OACA,QAAA,GACA,WAAA,UAAA,OAAA,OACA,gBAAA,KAAA,KlBxDE,4BkBkEC,6B9B0gIH,mC8BtgIQ,cAAA,EACA,aAAA,GlBpFN,yBkB+EA,kBAUI,cAAA,IAAA,OAAA,UAAA,IAAA,OACA,cAAA,MAAA,gBAAA,WAXH,8BAcK,mBAAA,IAAA,eAAA,IAdL,6CAiBO,SAAA,SAjBP,wCAqBO,cAAA,MACA,aAAA,MAtBP,6B9BmiIH,mC8BtgIQ,cAAA,OAAA,UAAA,OA7BL,mCAiCK,QAAA,sBAAA,QAAA,eAGA,wBAAA,KAAA,WAAA,KApCL,kCAwCK,QAAA,MlB1GN,4BkBkEC,6B9BojIH,mC8BhjIQ,cAAA,EACA,aAAA,GlBpFN,yBkB+EA,kBAUI,cAAA,IAAA,OAAA,UAAA,IAAA,OACA,cAAA,MAAA,gBAAA,WAXH,8BAcK,mBAAA,IAAA,eAAA,IAdL,6CAiBO,SAAA,SAjBP,wCAqBO,cAAA,MACA,aAAA,MAtBP,6B9B6kIH,mC8BhjIQ,cAAA,OAAA,UAAA,OA7BL,mCAiCK,QAAA,sBAAA,QAAA,eAGA,wBAAA,KAAA,WAAA,KApCL,kCAwCK,QAAA,MlB1GN,4BkBkEC,6B9B8lIH,mC8B1lIQ,cAAA,EACA,aAAA,GlBpFN,yBkB+EA,kBAUI,cAAA,IAAA,OAAA,UAAA,IAAA,OACA,cAAA,MAAA,gBAAA,WAXH,8BAcK,mBAAA,IAAA,eAAA,IAdL,6CAiBO,SAAA,SAjBP,wCAqBO,cAAA,MACA,aAAA,MAtBP,6B9BunIH,mC8B1lIQ,cAAA,OAAA,UAAA,OA7BL,mCAiCK,QAAA,sBAAA,QAAA,eAGA,wBAAA,KAAA,WAAA,KApCL,kCAwCK,QAAA,MlB1GN,6BkBkEC,6B9BwoIH,mC8BpoIQ,cAAA,EACA,aAAA,GlBpFN,0BkB+EA,kBAUI,cAAA,IAAA,OAAA,UAAA,IAAA,OACA,cAAA,MAAA,gBAAA,WAXH,8BAcK,mBAAA,IAAA,eAAA,IAdL,6CAiBO,SAAA,SAjBP,wCAqBO,cAAA,MACA,aAAA,MAtBP,6B9BiqIH,mC8BpoIQ,cAAA,OAAA,UAAA,OA7BL,mCAiCK,QAAA,sBAAA,QAAA,eAGA,wBAAA,KAAA,WAAA,KApCL,kCAwCK,QAAA,MA7CV,eAeQ,cAAA,IAAA,OAAA,UAAA,IAAA,OACA,cAAA,MAAA,gBAAA,WAhBR,0B9B6rIA,gC8BprIU,cAAA,EACA,aAAA,EAVV,2BAmBU,mBAAA,IAAA,eAAA,IAnBV,0CAsBY,SAAA,SAtBZ,qCA0BY,cAAA,MACA,aAAA,MA3BZ,0B9BitIA,gC8B/qIU,cAAA,OAAA,UAAA,OAlCV,gCAsCU,QAAA,sBAAA,QAAA,eAGA,wBAAA,KAAA,WAAA,KAzCV,+BA6CU,QAAA,KAaV,4BAEI,MAAA,e3BlLF,kCAAA,kC2BqLI,MAAA,eALN,oCAWM,MAAA,e3B3LJ,0CAAA,0C2B8LM,MAAA,eAdR,6CAkBQ,MAAA,e9B0qIR,4CAEA,2CADA,yC8B7rIA,0CA0BM,MAAA,eA1BN,8BA+BI,MAAA,eACA,aAAA,eAhCJ,mCAoCI,iBAAA,uOApCJ,2BAwCI,MAAA,eAxCJ,6BA0CM,MAAA,e3B1NJ,mCAAA,mC2B6NM,MAAA,eAOR,2BAEI,MAAA,K3BtOF,iCAAA,iC2ByOI,MAAA,KALN,mCAWM,MAAA,qB3B/OJ,yCAAA,yC2BkPM,MAAA,sBAdR,4CAkBQ,MAAA,sB9BsqIR,2CAEA,0CADA,wC8BzrIA,yCA0BM,MAAA,KA1BN,6BA+BI,MAAA,qBACA,aAAA,qBAhCJ,kCAoCI,iBAAA,6OApCJ,0BAwCI,MAAA,qBAxCJ,4BA0CM,MAAA,K3B9QJ,kCAAA,kC2BiRM,MAAA,KC7RR,MACE,SAAA,SACA,QAAA,YAAA,QAAA,KACA,mBAAA,OAAA,eAAA,OACA,UAAA,EACA,UAAA,WACA,iBAAA,KACA,gBAAA,WACA,OAAA,IAAA,MAAA,iBvBPE,cAAA,OuBDJ,SAYI,aAAA,EACA,YAAA,EAbJ,2DvBUI,uBAAA,OACA,wBAAA,OuBXJ,yDvBwBI,2BAAA,OACA,0BAAA,OuBIJ,WAGE,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,QAAA,QAIF,YACE,cAAA,OAGF,eACE,WAAA,SACA,cAAA,EAGF,sBACE,cAAA,E5BvCA,iB4B4CE,gBAAA,KAFJ,sBAMI,YAAA,QAQJ,aACE,QAAA,OAAA,QACA,cAAA,EAEA,iBAAA,gBACA,cAAA,IAAA,MAAA,iBALF,yBvB/DI,cAAA,mBAAA,mBAAA,EAAA,EuB+DJ,sDAaM,WAAA,EAKN,aACE,QAAA,OAAA,QACA,iBAAA,gBACA,WAAA,IAAA,MAAA,iBAHF,wBvBjFI,cAAA,EAAA,EAAA,mBAAA,mBuBgGJ,kBACE,aAAA,SACA,cAAA,QACA,YAAA,SACA,cAAA,EAGF,mBACE,aAAA,SACA,YAAA,SAIF,kBACE,SAAA,SACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,QAGF,UACE,MAAA,KvBvHE,cAAA,mBuB4HJ,cACE,MAAA,KvBpHE,uBAAA,mBACA,wBAAA,mBuBuHJ,iBACE,MAAA,KvB3GE,2BAAA,mBACA,0BAAA,mBuBiHJ,WACE,QAAA,YAAA,QAAA,KACA,mBAAA,OAAA,eAAA,OAFF,iBAKI,cAAA,KnBvFA,yBmBkFJ,WASI,cAAA,IAAA,KAAA,UAAA,IAAA,KACA,aAAA,MACA,YAAA,MAXJ,iBAcM,QAAA,YAAA,QAAA,KAEA,SAAA,EAAA,EAAA,GAAA,KAAA,EAAA,EAAA,GACA,mBAAA,OAAA,eAAA,OACA,aAAA,KACA,cAAA,EACA,YAAA,MAUN,YACE,QAAA,YAAA,QAAA,KACA,mBAAA,OAAA,eAAA,OAFF,kBAOI,cAAA,KnBvHA,yBmBgHJ,YAWI,cAAA,IAAA,KAAA,UAAA,IAAA,KAXJ,kBAgBM,SAAA,EAAA,EAAA,GAAA,KAAA,EAAA,EAAA,GACA,cAAA,EAjBN,wBAoBQ,YAAA,EACA,YAAA,EArBR,mCvBvJI,wBAAA,EACA,2BAAA,ERqmJF,gD+B/8IF,iDAgCY,wBAAA,E/Bm7IV,gD+Bn9IF,oDAqCY,2BAAA,EArCZ,oCvBzII,uBAAA,EACA,0BAAA,ERmmJF,iD+B39IF,kDA+CY,uBAAA,E/Bg7IV,iD+B/9IF,qDAoDY,0BAAA,GAaZ,oBAEI,cAAA,OnBnLA,yBmBiLJ,cAMI,qBAAA,EAAA,kBAAA,EAAA,aAAA,EACA,mBAAA,QAAA,gBAAA,QAAA,WAAA,QACA,QAAA,EACA,OAAA,EATJ,oBAYM,QAAA,aACA,MAAA,MAUN,iBAEI,SAAA,OAFJ,8DvB/PI,cAAA,EuB+PJ,wDAUQ,cAAA,EvBzQJ,cAAA,EuB+PJ,+BAgBM,cAAA,EvBxPF,2BAAA,EACA,0BAAA,EuBuOJ,8BvBtPI,uBAAA,EACA,wBAAA,EuBqPJ,8BAyBM,cAAA,KC7RN,YACE,QAAA,YAAA,QAAA,KACA,cAAA,KAAA,UAAA,KACA,QAAA,OAAA,KACA,cAAA,KACA,WAAA,KACA,iBAAA,QxBDE,cAAA,OwBKJ,kCAGI,aAAA,MAHJ,0CAMM,QAAA,aACA,cAAA,MACA,MAAA,QACA,QAAA,IATN,gDAoBI,gBAAA,UApBJ,gDAwBI,gBAAA,KAxBJ,wBA4BI,MAAA,QCtCJ,YACE,QAAA,YAAA,QAAA,K5BGA,aAAA,EACA,WAAA,KGAE,cAAA,OyBCJ,WACE,SAAA,SACA,QAAA,MACA,QAAA,MAAA,OACA,YAAA,KACA,YAAA,KACA,MAAA,QACA,iBAAA,KACA,OAAA,IAAA,MAAA,QARF,iBAWI,QAAA,EACA,MAAA,QACA,gBAAA,KACA,iBAAA,QACA,aAAA,QAfJ,iBAmBI,QAAA,EACA,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBAIJ,kCAGM,YAAA,EzBCF,uBAAA,OACA,0BAAA,OyBLJ,iCzBVI,wBAAA,OACA,2BAAA,OyBSJ,6BAcI,QAAA,EACA,MAAA,KACA,iBAAA,QACA,aAAA,QAjBJ,+BAqBI,MAAA,QACA,eAAA,KAEA,OAAA,KACA,iBAAA,KACA,aAAA,QCtDF,0BACE,QAAA,OAAA,OjC2HE,UAAA,QiCzHF,YAAA,IAKE,iD1BwBF,uBAAA,MACA,0BAAA,M0BpBE,gD1BKF,wBAAA,MACA,2BAAA,M0BnBF,0BACE,QAAA,OAAA,MjC2HE,UAAA,QiCzHF,YAAA,IAKE,iD1BwBF,uBAAA,MACA,0BAAA,M0BpBE,gD1BKF,wBAAA,MACA,2BAAA,M2BjBJ,OACE,QAAA,aACA,QAAA,MAAA,KlCiEE,UAAA,IkC/DF,YAAA,IACA,YAAA,EACA,WAAA,OACA,YAAA,OACA,eAAA,S3BRE,cAAA,OSCE,WAAA,MAAA,KAAA,WAAA,CAAA,iBAAA,KAAA,WAAA,CAAA,aAAA,KAAA,WAAA,CAAA,WAAA,KAAA,YAKF,uCkBNJ,OlBOM,WAAA,MdIJ,cAAA,cgCGI,gBAAA,KAdN,aAoBI,QAAA,KAKJ,YACE,SAAA,SACA,IAAA,KAOF,YACE,cAAA,KACA,aAAA,K3BpCE,cAAA,M2B6CF,eCjDA,MAAA,KACA,iBAAA,QjCcA,sBAAA,sBiCVI,MAAA,KACA,iBAAA,QAHI,sBAAA,sBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,mBDqCJ,iBCjDA,MAAA,KACA,iBAAA,QjCcA,wBAAA,wBiCVI,MAAA,KACA,iBAAA,QAHI,wBAAA,wBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,qBDqCJ,eCjDA,MAAA,KACA,iBAAA,QjCcA,sBAAA,sBiCVI,MAAA,KACA,iBAAA,QAHI,sBAAA,sBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,mBDqCJ,YCjDA,MAAA,KACA,iBAAA,QjCcA,mBAAA,mBiCVI,MAAA,KACA,iBAAA,QAHI,mBAAA,mBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,oBDqCJ,eCjDA,MAAA,QACA,iBAAA,QjCcA,sBAAA,sBiCVI,MAAA,QACA,iBAAA,QAHI,sBAAA,sBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,mBDqCJ,cCjDA,MAAA,KACA,iBAAA,QjCcA,qBAAA,qBiCVI,MAAA,KACA,iBAAA,QAHI,qBAAA,qBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,mBDqCJ,aCjDA,MAAA,QACA,iBAAA,QjCcA,oBAAA,oBiCVI,MAAA,QACA,iBAAA,QAHI,oBAAA,oBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,qBDqCJ,YCjDA,MAAA,KACA,iBAAA,QjCcA,mBAAA,mBiCVI,MAAA,KACA,iBAAA,QAHI,mBAAA,mBAQJ,QAAA,EACA,WAAA,EAAA,EAAA,EAAA,MAAA,kBCbN,WACE,QAAA,KAAA,KACA,cAAA,KAEA,iBAAA,Q7BCE,cAAA,MIuDA,yByB5DJ,WAQI,QAAA,KAAA,MAIJ,iBACE,cAAA,EACA,aAAA,E7BTE,cAAA,E8BDJ,OACE,SAAA,SACA,QAAA,OAAA,QACA,cAAA,KACA,OAAA,IAAA,MAAA,Y9BHE,cAAA,O8BQJ,eAEE,MAAA,QAIF,YACE,YAAA,IAQF,mBACE,cAAA,KADF,0BAKI,SAAA,SACA,IAAA,EACA,MAAA,EACA,QAAA,OAAA,QACA,MAAA,QAUF,eC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,kBACE,iBAAA,QAGF,2BACE,MAAA,QDqCF,iBC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,oBACE,iBAAA,QAGF,6BACE,MAAA,QDqCF,eC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,kBACE,iBAAA,QAGF,2BACE,MAAA,QDqCF,YC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,eACE,iBAAA,QAGF,wBACE,MAAA,QDqCF,eC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,kBACE,iBAAA,QAGF,2BACE,MAAA,QDqCF,cC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,iBACE,iBAAA,QAGF,0BACE,MAAA,QDqCF,aC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,gBACE,iBAAA,QAGF,yBACE,MAAA,QDqCF,YC9CA,MAAA,QpBKE,iBAAA,QoBHF,aAAA,QAEA,eACE,iBAAA,QAGF,wBACE,MAAA,QCRF,wCACE,KAAO,oBAAA,KAAA,EACP,GAAK,oBAAA,EAAA,GAFP,gCACE,KAAO,oBAAA,KAAA,EACP,GAAK,oBAAA,EAAA,GAIT,UACE,QAAA,YAAA,QAAA,KACA,OAAA,KACA,SAAA,OvCoHI,UAAA,OuClHJ,iBAAA,QhCRE,cAAA,OgCaJ,cACE,QAAA,YAAA,QAAA,KACA,mBAAA,OAAA,eAAA,OACA,cAAA,OAAA,gBAAA,OACA,MAAA,KACA,WAAA,OACA,YAAA,OACA,iBAAA,QvBnBI,WAAA,MAAA,IAAA,KAKF,uCuBOJ,cvBNM,WAAA,MuBiBN,sBrBcE,iBAAA,iKqBZA,gBAAA,KAAA,KAIA,uBACE,kBAAA,qBAAA,GAAA,OAAA,SAAA,UAAA,qBAAA,GAAA,OAAA,SAEA,uCAHF,uBAII,kBAAA,KAAA,UAAA,MCvCN,OACE,QAAA,YAAA,QAAA,KACA,eAAA,MAAA,YAAA,WAGF,YACE,SAAA,EAAA,KAAA,ECFF,YACE,QAAA,YAAA,QAAA,KACA,mBAAA,OAAA,eAAA,OAGA,aAAA,EACA,cAAA,EASF,wBACE,MAAA,KACA,MAAA,QACA,WAAA,QvCNA,8BAAA,8BuCUE,QAAA,EACA,MAAA,QACA,gBAAA,KACA,iBAAA,QAVJ,+BAcI,MAAA,QACA,iBAAA,QASJ,iBACE,SAAA,SACA,QAAA,MACA,QAAA,OAAA,QAEA,cAAA,KAEA,iBAAA,KACA,OAAA,IAAA,MAAA,iBARF,6BlC7BI,uBAAA,OACA,wBAAA,OkC4BJ,4BAeI,cAAA,ElC9BA,2BAAA,OACA,0BAAA,OkCcJ,0BAAA,0BAqBI,MAAA,QACA,eAAA,KACA,iBAAA,KAvBJ,wBA4BI,QAAA,EACA,MAAA,KACA,iBAAA,QACA,aAAA,QAaA,uBACE,mBAAA,IAAA,eAAA,IADF,wCAII,aAAA,KACA,cAAA,EALJ,oDlCpDA,uBAAA,OACA,0BAAA,OAYA,wBAAA,EkCuCA,mDAaM,aAAA,ElC/EN,wBAAA,OACA,2BAAA,OAsCA,0BAAA,EIAA,yB8B2BA,0BACE,mBAAA,IAAA,eAAA,IADF,2CAII,aAAA,KACA,cAAA,EALJ,uDlCpDA,uBAAA,OACA,0BAAA,OAYA,wBAAA,EkCuCA,sDAaM,aAAA,ElC/EN,wBAAA,OACA,2BAAA,OAsCA,0BAAA,GIAA,yB8B2BA,0BACE,mBAAA,IAAA,eAAA,IADF,2CAII,aAAA,KACA,cAAA,EALJ,uDlCpDA,uBAAA,OACA,0BAAA,OAYA,wBAAA,EkCuCA,sDAaM,aAAA,ElC/EN,wBAAA,OACA,2BAAA,OAsCA,0BAAA,GIAA,yB8B2BA,0BACE,mBAAA,IAAA,eAAA,IADF,2CAII,aAAA,KACA,cAAA,EALJ,uDlCpDA,uBAAA,OACA,0BAAA,OAYA,wBAAA,EkCuCA,sDAaM,aAAA,ElC/EN,wBAAA,OACA,2BAAA,OAsCA,0BAAA,GIAA,0B8B2BA,0BACE,mBAAA,IAAA,eAAA,IADF,2CAII,aAAA,KACA,cAAA,EALJ,uDlCpDA,uBAAA,OACA,0BAAA,OAYA,wBAAA,EkCuCA,sDAaM,aAAA,ElC/EN,wBAAA,OACA,2BAAA,OAsCA,0BAAA,GkCuDJ,mCAEI,aAAA,EACA,YAAA,ElCjHA,cAAA,EkC8GJ,8CAOM,cAAA,KAPN,2DAaM,WAAA,EAbN,yDAmBM,cAAA,EACA,cAAA,ECpIJ,yBACE,MAAA,QACA,iBAAA,QxCWF,sDAAA,sDwCPM,MAAA,QACA,iBAAA,QAPN,uDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QAbN,2BACE,MAAA,QACA,iBAAA,QxCWF,wDAAA,wDwCPM,MAAA,QACA,iBAAA,QAPN,yDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QAbN,yBACE,MAAA,QACA,iBAAA,QxCWF,sDAAA,sDwCPM,MAAA,QACA,iBAAA,QAPN,uDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QAbN,sBACE,MAAA,QACA,iBAAA,QxCWF,mDAAA,mDwCPM,MAAA,QACA,iBAAA,QAPN,oDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QAbN,yBACE,MAAA,QACA,iBAAA,QxCWF,sDAAA,sDwCPM,MAAA,QACA,iBAAA,QAPN,uDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QAbN,wBACE,MAAA,QACA,iBAAA,QxCWF,qDAAA,qDwCPM,MAAA,QACA,iBAAA,QAPN,sDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QAbN,uBACE,MAAA,QACA,iBAAA,QxCWF,oDAAA,oDwCPM,MAAA,QACA,iBAAA,QAPN,qDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QAbN,sBACE,MAAA,QACA,iBAAA,QxCWF,mDAAA,mDwCPM,MAAA,QACA,iBAAA,QAPN,oDAWM,MAAA,KACA,iBAAA,QACA,aAAA,QChBR,OACE,MAAA,M3C8HI,UAAA,O2C5HJ,YAAA,IACA,YAAA,EACA,MAAA,KACA,YAAA,EAAA,IAAA,EAAA,KACA,QAAA,GzCKA,ayCDE,MAAA,KACA,gBAAA,KzCIF,2CAAA,2CyCCI,QAAA,IAWN,aACE,QAAA,EACA,iBAAA,YACA,OAAA,EACA,mBAAA,KAAA,gBAAA,KAAA,WAAA,KAMF,iBACE,eAAA,KCvCF,OACE,UAAA,MACA,SAAA,O5C6HI,UAAA,Q4C1HJ,iBAAA,sBACA,gBAAA,YACA,OAAA,IAAA,MAAA,eACA,WAAA,EAAA,OAAA,OAAA,eACA,wBAAA,WAAA,gBAAA,WACA,QAAA,ErCLE,cAAA,OqCLJ,wBAcI,cAAA,OAdJ,eAkBI,QAAA,EAlBJ,YAsBI,QAAA,MACA,QAAA,EAvBJ,YA2BI,QAAA,KAIJ,cACE,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OACA,QAAA,OAAA,OACA,MAAA,QACA,iBAAA,sBACA,gBAAA,YACA,cAAA,IAAA,MAAA,gBAGF,YACE,QAAA,OCpCF,YAEE,SAAA,OAFF,mBAKI,WAAA,OACA,WAAA,KAKJ,OACE,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,QAAA,KACA,MAAA,KACA,OAAA,KACA,SAAA,OAGA,QAAA,EAOF,cACE,SAAA,SACA,MAAA,KACA,OAAA,MAEA,eAAA,KAGA,0B7BrCI,WAAA,kBAAA,IAAA,SAAA,WAAA,UAAA,IAAA,SAAA,WAAA,UAAA,IAAA,QAAA,CAAA,kBAAA,IAAA,S6BuCF,kBAAA,mBAAA,UAAA,mB7BlCA,uC6BgCF,0B7B/BI,WAAA,M6BmCJ,0BACE,kBAAA,KAAA,UAAA,KAIJ,yBACE,QAAA,YAAA,QAAA,KACA,WAAA,kBAFF,wCAKI,WAAA,mBACA,SAAA,O9CulLJ,uC8C7lLA,uCAWI,kBAAA,EAAA,YAAA,EAXJ,qCAeI,WAAA,KAIJ,uBACE,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OACA,WAAA,kBAHF,+BAOI,QAAA,MACA,OAAA,mBACA,QAAA,GATJ,+CAcI,mBAAA,OAAA,eAAA,OACA,cAAA,OAAA,gBAAA,OACA,OAAA,KAhBJ,8DAmBM,WAAA,KAnBN,uDAuBM,QAAA,KAMN,eACE,SAAA,SACA,QAAA,YAAA,QAAA,KACA,mBAAA,OAAA,eAAA,OACA,MAAA,KAGA,eAAA,KACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,etCzGE,cAAA,MsC6GF,QAAA,EAIF,gBACE,SAAA,MACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,MAAA,MACA,OAAA,MACA,iBAAA,KAPF,qBAUW,QAAA,EAVX,qBAWW,QAAA,GAKX,cACE,QAAA,YAAA,QAAA,KACA,eAAA,MAAA,YAAA,WACA,cAAA,QAAA,gBAAA,cACA,QAAA,KAAA,KACA,cAAA,IAAA,MAAA,QtC7HE,uBAAA,MACA,wBAAA,MsCuHJ,qBASI,QAAA,KAAA,KAEA,OAAA,MAAA,MAAA,MAAA,KAKJ,aACE,cAAA,EACA,YAAA,IAKF,YACE,SAAA,SAGA,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,QAAA,KAIF,cACE,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OACA,cAAA,IAAA,gBAAA,SACA,QAAA,KACA,WAAA,IAAA,MAAA,QtC/IE,2BAAA,MACA,0BAAA,MsCyIJ,iCASyB,YAAA,OATzB,gCAUwB,aAAA,OAIxB,yBACE,SAAA,SACA,IAAA,QACA,MAAA,KACA,OAAA,KACA,SAAA,OlC7HE,yBkCzBJ,cA6JI,UAAA,MACA,OAAA,QAAA,KA7IJ,yBAiJI,WAAA,oBAjJJ,wCAoJM,WAAA,qBAjIN,uBAsII,WAAA,oBAtIJ,+BAyIM,OAAA,qBAQJ,UAAY,UAAA,OlC5JV,yBkCgKF,U9CglLA,U8C9kLE,UAAA,OlClKA,0BkCuKF,UAAY,UAAA,QClOd,SACE,SAAA,SACA,QAAA,KACA,QAAA,MACA,OAAA,ECJA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBAEA,WAAA,OACA,YAAA,IACA,YAAA,IACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,OACA,WAAA,OACA,aAAA,OACA,YAAA,OACA,WAAA,K/CgHI,UAAA,Q8CpHJ,UAAA,WACA,QAAA,EAXF,cAaW,QAAA,GAbX,gBAgBI,SAAA,SACA,QAAA,MACA,MAAA,MACA,OAAA,MAnBJ,wBAsBM,SAAA,SACA,QAAA,GACA,aAAA,YACA,aAAA,MAKN,mCAAA,gBACE,QAAA,MAAA,EADF,0CAAA,uBAII,OAAA,EAJJ,kDAAA,+BAOM,IAAA,EACA,aAAA,MAAA,MAAA,EACA,iBAAA,KAKN,qCAAA,kBACE,QAAA,EAAA,MADF,4CAAA,yBAII,KAAA,EACA,MAAA,MACA,OAAA,MANJ,oDAAA,iCASM,MAAA,EACA,aAAA,MAAA,MAAA,MAAA,EACA,mBAAA,KAKN,sCAAA,mBACE,QAAA,MAAA,EADF,6CAAA,0BAII,IAAA,EAJJ,qDAAA,kCAOM,OAAA,EACA,aAAA,EAAA,MAAA,MACA,oBAAA,KAKN,oCAAA,iBACE,QAAA,EAAA,MADF,2CAAA,wBAII,MAAA,EACA,MAAA,MACA,OAAA,MANJ,mDAAA,gCASM,KAAA,EACA,aAAA,MAAA,EAAA,MAAA,MACA,kBAAA,KAqBN,eACE,UAAA,MACA,QAAA,OAAA,MACA,MAAA,KACA,WAAA,OACA,iBAAA,KvC3GE,cAAA,OyCLJ,SACE,SAAA,SACA,IAAA,EACA,KAAA,EACA,QAAA,KACA,QAAA,MACA,UAAA,MDLA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBAEA,WAAA,OACA,YAAA,IACA,YAAA,IACA,WAAA,KACA,WAAA,MACA,gBAAA,KACA,YAAA,KACA,eAAA,KACA,eAAA,OACA,WAAA,OACA,aAAA,OACA,YAAA,OACA,WAAA,K/CgHI,UAAA,QgDnHJ,UAAA,WACA,iBAAA,KACA,gBAAA,YACA,OAAA,IAAA,MAAA,ezCVE,cAAA,MyCLJ,gBAoBI,SAAA,SACA,QAAA,MACA,MAAA,KACA,OAAA,MACA,OAAA,EAAA,MAxBJ,uBAAA,wBA4BM,SAAA,SACA,QAAA,MACA,QAAA,GACA,aAAA,YACA,aAAA,MAKN,mCAAA,gBACE,cAAA,MADF,0CAAA,uBAII,OAAA,yBAJJ,kDAAA,+BAOM,OAAA,EACA,aAAA,MAAA,MAAA,EACA,iBAAA,gBATN,iDAAA,8BAaM,OAAA,IACA,aAAA,MAAA,MAAA,EACA,iBAAA,KAKN,qCAAA,kBACE,YAAA,MADF,4CAAA,yBAII,KAAA,yBACA,MAAA,MACA,OAAA,KACA,OAAA,MAAA,EAPJ,oDAAA,iCAUM,KAAA,EACA,aAAA,MAAA,MAAA,MAAA,EACA,mBAAA,gBAZN,mDAAA,gCAgBM,KAAA,IACA,aAAA,MAAA,MAAA,MAAA,EACA,mBAAA,KAKN,sCAAA,mBACE,WAAA,MADF,6CAAA,0BAII,IAAA,yBAJJ,qDAAA,kCAOM,IAAA,EACA,aAAA,EAAA,MAAA,MAAA,MACA,oBAAA,gBATN,oDAAA,iCAaM,IAAA,IACA,aAAA,EAAA,MAAA,MAAA,MACA,oBAAA,KAfN,8DAAA,2CAqBI,SAAA,SACA,IAAA,EACA,KAAA,IACA,QAAA,MACA,MAAA,KACA,YAAA,OACA,QAAA,GACA,cAAA,IAAA,MAAA,QAIJ,oCAAA,iBACE,aAAA,MADF,2CAAA,wBAII,MAAA,yBACA,MAAA,MACA,OAAA,KACA,OAAA,MAAA,EAPJ,mDAAA,gCAUM,MAAA,EACA,aAAA,MAAA,EAAA,MAAA,MACA,kBAAA,gBAZN,kDAAA,+BAgBM,MAAA,IACA,aAAA,MAAA,EAAA,MAAA,MACA,kBAAA,KAsBN,gBACE,QAAA,MAAA,OACA,cAAA,EhD3BI,UAAA,KgD8BJ,iBAAA,QACA,cAAA,IAAA,MAAA,QzChJE,uBAAA,kBACA,wBAAA,kByCyIJ,sBAWI,QAAA,KAIJ,cACE,QAAA,MAAA,OACA,MAAA,QC5JF,UACE,SAAA,SAGF,wBACE,iBAAA,MAAA,aAAA,MAGF,gBACE,SAAA,SACA,MAAA,KACA,SAAA,OCvBA,uBACE,QAAA,MACA,MAAA,KACA,QAAA,GDwBJ,eACE,SAAA,SACA,QAAA,KACA,MAAA,KACA,MAAA,KACA,aAAA,MACA,4BAAA,OAAA,oBAAA,OjC5BI,WAAA,kBAAA,IAAA,YAAA,WAAA,UAAA,IAAA,YAAA,WAAA,UAAA,IAAA,WAAA,CAAA,kBAAA,IAAA,YAKF,uCiCiBJ,ejChBM,WAAA,MjBomMN,oBACA,oBkD3kMA,sBAGE,QAAA,MlD6kMF,4BkD1kMA,6CAEE,kBAAA,iBAAA,UAAA,iBlD8kMF,2BkD3kMA,8CAEE,kBAAA,kBAAA,UAAA,kBAQF,8BAEI,QAAA,EACA,oBAAA,QACA,kBAAA,KAAA,UAAA,KlD0kMJ,sDACA,uDkD/kMA,qCAUI,QAAA,EACA,QAAA,EAXJ,0ClDqlMA,2CkDrkMI,QAAA,EACA,QAAA,EjCtEE,WAAA,GAAA,IAAA,QAKF,uCiCgDJ,0ClD6lME,2CiB5oMI,WAAA,MjBkpMN,uBkDxkMA,uBAEE,SAAA,SACA,IAAA,EACA,OAAA,EACA,QAAA,EAEA,QAAA,YAAA,QAAA,KACA,eAAA,OAAA,YAAA,OACA,cAAA,OAAA,gBAAA,OACA,MAAA,IACA,MAAA,KACA,WAAA,OACA,QAAA,GjC7FI,WAAA,QAAA,KAAA,KAKF,uCjBuqMF,uBkD5lMF,uBjC1EM,WAAA,MjB6qMN,6BADA,6BGxqME,6BAAA,6B+CwFE,MAAA,KACA,gBAAA,KACA,QAAA,EACA,QAAA,GAGJ,uBACE,KAAA,EAKF,uBACE,MAAA,ElDolMF,4BkD7kMA,4BAEE,QAAA,aACA,MAAA,KACA,OAAA,KACA,WAAA,UAAA,GAAA,CAAA,KAAA,KAEF,4BACE,iBAAA,kLAEF,4BACE,iBAAA,kLASF,qBACE,SAAA,SACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,GACA,QAAA,YAAA,QAAA,KACA,cAAA,OAAA,gBAAA,OACA,aAAA,EAEA,aAAA,IACA,YAAA,IACA,WAAA,KAZF,wBAeI,WAAA,YACA,SAAA,EAAA,EAAA,KAAA,KAAA,EAAA,EAAA,KACA,MAAA,KACA,OAAA,IACA,aAAA,IACA,YAAA,IACA,YAAA,OACA,OAAA,QACA,iBAAA,KACA,gBAAA,YAEA,WAAA,KAAA,MAAA,YACA,cAAA,KAAA,MAAA,YACA,QAAA,GjCtKE,WAAA,QAAA,IAAA,KAKF,uCiCqIJ,wBjCpIM,WAAA,MiCoIN,6BAiCI,QAAA,EASJ,kBACE,SAAA,SACA,MAAA,IACA,OAAA,KACA,KAAA,IACA,QAAA,GACA,YAAA,KACA,eAAA,KACA,MAAA,KACA,WAAA,OE/LF,kCACE,GAAK,kBAAA,eAAA,UAAA,gBADP,0BACE,GAAK,kBAAA,eAAA,UAAA,gBAGP,gBACE,QAAA,aACA,MAAA,KACA,OAAA,KACA,eAAA,YACA,OAAA,MAAA,MAAA,aACA,mBAAA,YAEA,cAAA,IACA,kBAAA,eAAA,KAAA,OAAA,SAAA,UAAA,eAAA,KAAA,OAAA,SAGF,mBACE,MAAA,KACA,OAAA,KACA,aAAA,KAOF,gCACE,GACE,kBAAA,SAAA,UAAA,SAEF,IACE,QAAA,GALJ,wBACE,GACE,kBAAA,SAAA,UAAA,SAEF,IACE,QAAA,GAIJ,cACE,QAAA,aACA,MAAA,KACA,OAAA,KACA,eAAA,YACA,iBAAA,aAEA,cAAA,IACA,QAAA,EACA,kBAAA,aAAA,KAAA,OAAA,SAAA,UAAA,aAAA,KAAA,OAAA,SAGF,iBACE,MAAA,KACA,OAAA,KCnDF,gBAAqB,eAAA,mBACrB,WAAqB,eAAA,cACrB,cAAqB,eAAA,iBACrB,cAAqB,eAAA,iBACrB,mBAAqB,eAAA,sBACrB,gBAAqB,eAAA,mBCFnB,YACE,iBAAA,kBnDUF,mBAAA,mBHm2MF,wBADA,wBsDv2MM,iBAAA,kBANJ,cACE,iBAAA,kBnDUF,qBAAA,qBH62MF,0BADA,0BsDj3MM,iBAAA,kBANJ,YACE,iBAAA,kBnDUF,mBAAA,mBHu3MF,wBADA,wBsD33MM,iBAAA,kBANJ,SACE,iBAAA,kBnDUF,gBAAA,gBHi4MF,qBADA,qBsDr4MM,iBAAA,kBANJ,YACE,iBAAA,kBnDUF,mBAAA,mBH24MF,wBADA,wBsD/4MM,iBAAA,kBANJ,WACE,iBAAA,kBnDUF,kBAAA,kBHq5MF,uBADA,uBsDz5MM,iBAAA,kBANJ,UACE,iBAAA,kBnDUF,iBAAA,iBH+5MF,sBADA,sBsDn6MM,iBAAA,kBANJ,SACE,iBAAA,kBnDUF,gBAAA,gBHy6MF,qBADA,qBsD76MM,iBAAA,kBCCN,UACE,iBAAA,eAGF,gBACE,iBAAA,sBCXF,QAAkB,OAAA,IAAA,MAAA,kBAClB,YAAkB,WAAA,IAAA,MAAA,kBAClB,cAAkB,aAAA,IAAA,MAAA,kBAClB,eAAkB,cAAA,IAAA,MAAA,kBAClB,aAAkB,YAAA,IAAA,MAAA,kBAElB,UAAmB,OAAA,YACnB,cAAmB,WAAA,YACnB,gBAAmB,aAAA,YACnB,iBAAmB,cAAA,YACnB,eAAmB,YAAA,YAGjB,gBACE,aAAA,kBADF,kBACE,aAAA,kBADF,gBACE,aAAA,kBADF,aACE,aAAA,kBADF,gBACE,aAAA,kBADF,eACE,aAAA,kBADF,cACE,aAAA,kBADF,aACE,aAAA,kBAIJ,cACE,aAAA,eAOF,YACE,cAAA,gBAGF,SACE,cAAA,iBAGF,aACE,uBAAA,iBACA,wBAAA,iBAGF,eACE,wBAAA,iBACA,2BAAA,iBAGF,gBACE,2BAAA,iBACA,0BAAA,iBAGF,cACE,uBAAA,iBACA,0BAAA,iBAGF,YACE,cAAA,gBAGF,gBACE,cAAA,cAGF,cACE,cAAA,gBAGF,WACE,cAAA,YLxEA,iBACE,QAAA,MACA,MAAA,KACA,QAAA,GMOE,QAAwB,QAAA,eAAxB,UAAwB,QAAA,iBAAxB,gBAAwB,QAAA,uBAAxB,SAAwB,QAAA,gBAAxB,SAAwB,QAAA,gBAAxB,aAAwB,QAAA,oBAAxB,cAAwB,QAAA,qBAAxB,QAAwB,QAAA,sBAAA,QAAA,eAAxB,eAAwB,QAAA,6BAAA,QAAA,sB7CiD1B,yB6CjDE,WAAwB,QAAA,eAAxB,aAAwB,QAAA,iBAAxB,mBAAwB,QAAA,uBAAxB,YAAwB,QAAA,gBAAxB,YAAwB,QAAA,gBAAxB,gBAAwB,QAAA,oBAAxB,iBAAwB,QAAA,qBAAxB,WAAwB,QAAA,sBAAA,QAAA,eAAxB,kBAAwB,QAAA,6BAAA,QAAA,uB7CiD1B,yB6CjDE,WAAwB,QAAA,eAAxB,aAAwB,QAAA,iBAAxB,mBAAwB,QAAA,uBAAxB,YAAwB,QAAA,gBAAxB,YAAwB,QAAA,gBAAxB,gBAAwB,QAAA,oBAAxB,iBAAwB,QAAA,qBAAxB,WAAwB,QAAA,sBAAA,QAAA,eAAxB,kBAAwB,QAAA,6BAAA,QAAA,uB7CiD1B,yB6CjDE,WAAwB,QAAA,eAAxB,aAAwB,QAAA,iBAAxB,mBAAwB,QAAA,uBAAxB,YAAwB,QAAA,gBAAxB,YAAwB,QAAA,gBAAxB,gBAAwB,QAAA,oBAAxB,iBAAwB,QAAA,qBAAxB,WAAwB,QAAA,sBAAA,QAAA,eAAxB,kBAAwB,QAAA,6BAAA,QAAA,uB7CiD1B,0B6CjDE,WAAwB,QAAA,eAAxB,aAAwB,QAAA,iBAAxB,mBAAwB,QAAA,uBAAxB,YAAwB,QAAA,gBAAxB,YAAwB,QAAA,gBAAxB,gBAAwB,QAAA,oBAAxB,iBAAwB,QAAA,qBAAxB,WAAwB,QAAA,sBAAA,QAAA,eAAxB,kBAAwB,QAAA,6BAAA,QAAA,uBAU9B,aAEI,cAAqB,QAAA,eAArB,gBAAqB,QAAA,iBAArB,sBAAqB,QAAA,uBAArB,eAAqB,QAAA,gBAArB,eAAqB,QAAA,gBAArB,mBAAqB,QAAA,oBAArB,oBAAqB,QAAA,qBAArB,cAAqB,QAAA,sBAAA,QAAA,eAArB,qBAAqB,QAAA,6BAAA,QAAA,uBCrBzB,kBACE,SAAA,SACA,QAAA,MACA,MAAA,KACA,QAAA,EACA,SAAA,OALF,0BAQI,QAAA,MACA,QAAA,GATJ,yC1DsxNA,wBADA,yBAEA,yBACA,wB0DvwNI,SAAA,SACA,IAAA,EACA,OAAA,EACA,KAAA,EACA,MAAA,KACA,OAAA,KACA,OAAA,EAQF,gCAEI,YAAA,WAFJ,gCAEI,YAAA,OAFJ,+BAEI,YAAA,IAFJ,+BAEI,YAAA,KCzBF,UAAgC,mBAAA,cAAA,eAAA,cAChC,aAAgC,mBAAA,iBAAA,eAAA,iBAChC,kBAAgC,mBAAA,sBAAA,eAAA,sBAChC,qBAAgC,mBAAA,yBAAA,eAAA,yBAEhC,WAA8B,cAAA,eAAA,UAAA,eAC9B,aAA8B,cAAA,iBAAA,UAAA,iBAC9B,mBAA8B,cAAA,uBAAA,UAAA,uBAC9B,WAA8B,SAAA,EAAA,EAAA,eAAA,KAAA,EAAA,EAAA,eAC9B,aAA8B,kBAAA,YAAA,UAAA,YAC9B,aAA8B,kBAAA,YAAA,UAAA,YAC9B,eAA8B,kBAAA,YAAA,YAAA,YAC9B,eAA8B,kBAAA,YAAA,YAAA,YAE9B,uBAAoC,cAAA,gBAAA,gBAAA,qBACpC,qBAAoC,cAAA,cAAA,gBAAA,mBACpC,wBAAoC,cAAA,iBAAA,gBAAA,iBACpC,yBAAoC,cAAA,kBAAA,gBAAA,wBACpC,wBAAoC,cAAA,qBAAA,gBAAA,uBAEpC,mBAAiC,eAAA,gBAAA,YAAA,qBACjC,iBAAiC,eAAA,cAAA,YAAA,mBACjC,oBAAiC,eAAA,iBAAA,YAAA,iBACjC,sBAAiC,eAAA,mBAAA,YAAA,mBACjC,qBAAiC,eAAA,kBAAA,YAAA,kBAEjC,qBAAkC,mBAAA,gBAAA,cAAA,qBAClC,mBAAkC,mBAAA,cAAA,cAAA,mBAClC,sBAAkC,mBAAA,iBAAA,cAAA,iBAClC,uBAAkC,mBAAA,kBAAA,cAAA,wBAClC,sBAAkC,mBAAA,qBAAA,cAAA,uBAClC,uBAAkC,mBAAA,kBAAA,cAAA,kBAElC,iBAAgC,oBAAA,eAAA,WAAA,eAChC,kBAAgC,oBAAA,gBAAA,WAAA,qBAChC,gBAAgC,oBAAA,cAAA,WAAA,mBAChC,mBAAgC,oBAAA,iBAAA,WAAA,iBAChC,qBAAgC,oBAAA,mBAAA,WAAA,mBAChC,oBAAgC,oBAAA,kBAAA,WAAA,kB/CYhC,yB+ClDA,aAAgC,mBAAA,cAAA,eAAA,cAChC,gBAAgC,mBAAA,iBAAA,eAAA,iBAChC,qBAAgC,mBAAA,sBAAA,eAAA,sBAChC,wBAAgC,mBAAA,yBAAA,eAAA,yBAEhC,cAA8B,cAAA,eAAA,UAAA,eAC9B,gBAA8B,cAAA,iBAAA,UAAA,iBAC9B,sBAA8B,cAAA,uBAAA,UAAA,uBAC9B,cAA8B,SAAA,EAAA,EAAA,eAAA,KAAA,EAAA,EAAA,eAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAE9B,0BAAoC,cAAA,gBAAA,gBAAA,qBACpC,wBAAoC,cAAA,cAAA,gBAAA,mBACpC,2BAAoC,cAAA,iBAAA,gBAAA,iBACpC,4BAAoC,cAAA,kBAAA,gBAAA,wBACpC,2BAAoC,cAAA,qBAAA,gBAAA,uBAEpC,sBAAiC,eAAA,gBAAA,YAAA,qBACjC,oBAAiC,eAAA,cAAA,YAAA,mBACjC,uBAAiC,eAAA,iBAAA,YAAA,iBACjC,yBAAiC,eAAA,mBAAA,YAAA,mBACjC,wBAAiC,eAAA,kBAAA,YAAA,kBAEjC,wBAAkC,mBAAA,gBAAA,cAAA,qBAClC,sBAAkC,mBAAA,cAAA,cAAA,mBAClC,yBAAkC,mBAAA,iBAAA,cAAA,iBAClC,0BAAkC,mBAAA,kBAAA,cAAA,wBAClC,yBAAkC,mBAAA,qBAAA,cAAA,uBAClC,0BAAkC,mBAAA,kBAAA,cAAA,kBAElC,oBAAgC,oBAAA,eAAA,WAAA,eAChC,qBAAgC,oBAAA,gBAAA,WAAA,qBAChC,mBAAgC,oBAAA,cAAA,WAAA,mBAChC,sBAAgC,oBAAA,iBAAA,WAAA,iBAChC,wBAAgC,oBAAA,mBAAA,WAAA,mBAChC,uBAAgC,oBAAA,kBAAA,WAAA,mB/CYhC,yB+ClDA,aAAgC,mBAAA,cAAA,eAAA,cAChC,gBAAgC,mBAAA,iBAAA,eAAA,iBAChC,qBAAgC,mBAAA,sBAAA,eAAA,sBAChC,wBAAgC,mBAAA,yBAAA,eAAA,yBAEhC,cAA8B,cAAA,eAAA,UAAA,eAC9B,gBAA8B,cAAA,iBAAA,UAAA,iBAC9B,sBAA8B,cAAA,uBAAA,UAAA,uBAC9B,cAA8B,SAAA,EAAA,EAAA,eAAA,KAAA,EAAA,EAAA,eAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAE9B,0BAAoC,cAAA,gBAAA,gBAAA,qBACpC,wBAAoC,cAAA,cAAA,gBAAA,mBACpC,2BAAoC,cAAA,iBAAA,gBAAA,iBACpC,4BAAoC,cAAA,kBAAA,gBAAA,wBACpC,2BAAoC,cAAA,qBAAA,gBAAA,uBAEpC,sBAAiC,eAAA,gBAAA,YAAA,qBACjC,oBAAiC,eAAA,cAAA,YAAA,mBACjC,uBAAiC,eAAA,iBAAA,YAAA,iBACjC,yBAAiC,eAAA,mBAAA,YAAA,mBACjC,wBAAiC,eAAA,kBAAA,YAAA,kBAEjC,wBAAkC,mBAAA,gBAAA,cAAA,qBAClC,sBAAkC,mBAAA,cAAA,cAAA,mBAClC,yBAAkC,mBAAA,iBAAA,cAAA,iBAClC,0BAAkC,mBAAA,kBAAA,cAAA,wBAClC,yBAAkC,mBAAA,qBAAA,cAAA,uBAClC,0BAAkC,mBAAA,kBAAA,cAAA,kBAElC,oBAAgC,oBAAA,eAAA,WAAA,eAChC,qBAAgC,oBAAA,gBAAA,WAAA,qBAChC,mBAAgC,oBAAA,cAAA,WAAA,mBAChC,sBAAgC,oBAAA,iBAAA,WAAA,iBAChC,wBAAgC,oBAAA,mBAAA,WAAA,mBAChC,uBAAgC,oBAAA,kBAAA,WAAA,mB/CYhC,yB+ClDA,aAAgC,mBAAA,cAAA,eAAA,cAChC,gBAAgC,mBAAA,iBAAA,eAAA,iBAChC,qBAAgC,mBAAA,sBAAA,eAAA,sBAChC,wBAAgC,mBAAA,yBAAA,eAAA,yBAEhC,cAA8B,cAAA,eAAA,UAAA,eAC9B,gBAA8B,cAAA,iBAAA,UAAA,iBAC9B,sBAA8B,cAAA,uBAAA,UAAA,uBAC9B,cAA8B,SAAA,EAAA,EAAA,eAAA,KAAA,EAAA,EAAA,eAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAE9B,0BAAoC,cAAA,gBAAA,gBAAA,qBACpC,wBAAoC,cAAA,cAAA,gBAAA,mBACpC,2BAAoC,cAAA,iBAAA,gBAAA,iBACpC,4BAAoC,cAAA,kBAAA,gBAAA,wBACpC,2BAAoC,cAAA,qBAAA,gBAAA,uBAEpC,sBAAiC,eAAA,gBAAA,YAAA,qBACjC,oBAAiC,eAAA,cAAA,YAAA,mBACjC,uBAAiC,eAAA,iBAAA,YAAA,iBACjC,yBAAiC,eAAA,mBAAA,YAAA,mBACjC,wBAAiC,eAAA,kBAAA,YAAA,kBAEjC,wBAAkC,mBAAA,gBAAA,cAAA,qBAClC,sBAAkC,mBAAA,cAAA,cAAA,mBAClC,yBAAkC,mBAAA,iBAAA,cAAA,iBAClC,0BAAkC,mBAAA,kBAAA,cAAA,wBAClC,yBAAkC,mBAAA,qBAAA,cAAA,uBAClC,0BAAkC,mBAAA,kBAAA,cAAA,kBAElC,oBAAgC,oBAAA,eAAA,WAAA,eAChC,qBAAgC,oBAAA,gBAAA,WAAA,qBAChC,mBAAgC,oBAAA,cAAA,WAAA,mBAChC,sBAAgC,oBAAA,iBAAA,WAAA,iBAChC,wBAAgC,oBAAA,mBAAA,WAAA,mBAChC,uBAAgC,oBAAA,kBAAA,WAAA,mB/CYhC,0B+ClDA,aAAgC,mBAAA,cAAA,eAAA,cAChC,gBAAgC,mBAAA,iBAAA,eAAA,iBAChC,qBAAgC,mBAAA,sBAAA,eAAA,sBAChC,wBAAgC,mBAAA,yBAAA,eAAA,yBAEhC,cAA8B,cAAA,eAAA,UAAA,eAC9B,gBAA8B,cAAA,iBAAA,UAAA,iBAC9B,sBAA8B,cAAA,uBAAA,UAAA,uBAC9B,cAA8B,SAAA,EAAA,EAAA,eAAA,KAAA,EAAA,EAAA,eAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,gBAA8B,kBAAA,YAAA,UAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAC9B,kBAA8B,kBAAA,YAAA,YAAA,YAE9B,0BAAoC,cAAA,gBAAA,gBAAA,qBACpC,wBAAoC,cAAA,cAAA,gBAAA,mBACpC,2BAAoC,cAAA,iBAAA,gBAAA,iBACpC,4BAAoC,cAAA,kBAAA,gBAAA,wBACpC,2BAAoC,cAAA,qBAAA,gBAAA,uBAEpC,sBAAiC,eAAA,gBAAA,YAAA,qBACjC,oBAAiC,eAAA,cAAA,YAAA,mBACjC,uBAAiC,eAAA,iBAAA,YAAA,iBACjC,yBAAiC,eAAA,mBAAA,YAAA,mBACjC,wBAAiC,eAAA,kBAAA,YAAA,kBAEjC,wBAAkC,mBAAA,gBAAA,cAAA,qBAClC,sBAAkC,mBAAA,cAAA,cAAA,mBAClC,yBAAkC,mBAAA,iBAAA,cAAA,iBAClC,0BAAkC,mBAAA,kBAAA,cAAA,wBAClC,yBAAkC,mBAAA,qBAAA,cAAA,uBAClC,0BAAkC,mBAAA,kBAAA,cAAA,kBAElC,oBAAgC,oBAAA,eAAA,WAAA,eAChC,qBAAgC,oBAAA,gBAAA,WAAA,qBAChC,mBAAgC,oBAAA,cAAA,WAAA,mBAChC,sBAAgC,oBAAA,iBAAA,WAAA,iBAChC,wBAAgC,oBAAA,mBAAA,WAAA,mBAChC,uBAAgC,oBAAA,kBAAA,WAAA,mBC1ChC,YAAwB,MAAA,eACxB,aAAwB,MAAA,gBACxB,YAAwB,MAAA,ehDoDxB,yBgDtDA,eAAwB,MAAA,eACxB,gBAAwB,MAAA,gBACxB,eAAwB,MAAA,gBhDoDxB,yBgDtDA,eAAwB,MAAA,eACxB,gBAAwB,MAAA,gBACxB,eAAwB,MAAA,gBhDoDxB,yBgDtDA,eAAwB,MAAA,eACxB,gBAAwB,MAAA,gBACxB,eAAwB,MAAA,gBhDoDxB,0BgDtDA,eAAwB,MAAA,eACxB,gBAAwB,MAAA,gBACxB,eAAwB,MAAA,gBCL1B,eAAsB,SAAA,eAAtB,iBAAsB,SAAA,iBCCtB,iBAAyB,SAAA,iBAAzB,mBAAyB,SAAA,mBAAzB,mBAAyB,SAAA,mBAAzB,gBAAyB,SAAA,gBAAzB,iBAAyB,SAAA,yBAAA,SAAA,iBAK3B,WACE,SAAA,MACA,IAAA,EACA,MAAA,EACA,KAAA,EACA,QAAA,KAGF,cACE,SAAA,MACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,KAI4B,2DAD9B,YAEI,SAAA,eAAA,SAAA,OACA,IAAA,EACA,QAAA,MCzBJ,SCEE,SAAA,SACA,MAAA,IACA,OAAA,IACA,QAAA,EACA,SAAA,OACA,KAAA,cACA,YAAA,OACA,OAAA,EAUA,0BAAA,yBAEE,SAAA,OACA,MAAA,KACA,OAAA,KACA,SAAA,QACA,KAAA,KACA,YAAA,OC5BJ,WAAa,WAAA,EAAA,QAAA,OAAA,2BACb,QAAU,WAAA,EAAA,MAAA,KAAA,0BACV,WAAa,WAAA,EAAA,KAAA,KAAA,2BACb,aAAe,WAAA,eCCX,MAAuB,MAAA,cAAvB,MAAuB,MAAA,cAAvB,MAAuB,MAAA,cAAvB,OAAuB,MAAA,eAAvB,QAAuB,MAAA,eAAvB,MAAuB,OAAA,cAAvB,MAAuB,OAAA,cAAvB,MAAuB,OAAA,cAAvB,OAAuB,OAAA,eAAvB,QAAuB,OAAA,eAI3B,QAAU,UAAA,eACV,QAAU,WAAA,eAIV,YAAc,UAAA,gBACd,YAAc,WAAA,gBAEd,QAAU,MAAA,gBACV,QAAU,OAAA,gBCfV,uBAEI,SAAA,SACA,IAAA,EACA,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,EAEA,eAAA,KACA,QAAA,GAEA,iBAAA,cCNI,KAAgC,OAAA,YAChC,MpEsuPR,MoEpuPU,WAAA,YAEF,MpEuuPR,MoEruPU,aAAA,YAEF,MpEwuPR,MoEtuPU,cAAA,YAEF,MpEyuPR,MoEvuPU,YAAA,YAfF,KAAgC,OAAA,iBAChC,MpE8vPR,MoE5vPU,WAAA,iBAEF,MpE+vPR,MoE7vPU,aAAA,iBAEF,MpEgwPR,MoE9vPU,cAAA,iBAEF,MpEiwPR,MoE/vPU,YAAA,iBAfF,KAAgC,OAAA,gBAChC,MpEsxPR,MoEpxPU,WAAA,gBAEF,MpEuxPR,MoErxPU,aAAA,gBAEF,MpEwxPR,MoEtxPU,cAAA,gBAEF,MpEyxPR,MoEvxPU,YAAA,gBAfF,KAAgC,OAAA,eAChC,MpE8yPR,MoE5yPU,WAAA,eAEF,MpE+yPR,MoE7yPU,aAAA,eAEF,MpEgzPR,MoE9yPU,cAAA,eAEF,MpEizPR,MoE/yPU,YAAA,eAfF,KAAgC,OAAA,iBAChC,MpEs0PR,MoEp0PU,WAAA,iBAEF,MpEu0PR,MoEr0PU,aAAA,iBAEF,MpEw0PR,MoEt0PU,cAAA,iBAEF,MpEy0PR,MoEv0PU,YAAA,iBAfF,KAAgC,OAAA,eAChC,MpE81PR,MoE51PU,WAAA,eAEF,MpE+1PR,MoE71PU,aAAA,eAEF,MpEg2PR,MoE91PU,cAAA,eAEF,MpEi2PR,MoE/1PU,YAAA,eAfF,KAAgC,QAAA,YAChC,MpEs3PR,MoEp3PU,YAAA,YAEF,MpEu3PR,MoEr3PU,cAAA,YAEF,MpEw3PR,MoEt3PU,eAAA,YAEF,MpEy3PR,MoEv3PU,aAAA,YAfF,KAAgC,QAAA,iBAChC,MpE84PR,MoE54PU,YAAA,iBAEF,MpE+4PR,MoE74PU,cAAA,iBAEF,MpEg5PR,MoE94PU,eAAA,iBAEF,MpEi5PR,MoE/4PU,aAAA,iBAfF,KAAgC,QAAA,gBAChC,MpEs6PR,MoEp6PU,YAAA,gBAEF,MpEu6PR,MoEr6PU,cAAA,gBAEF,MpEw6PR,MoEt6PU,eAAA,gBAEF,MpEy6PR,MoEv6PU,aAAA,gBAfF,KAAgC,QAAA,eAChC,MpE87PR,MoE57PU,YAAA,eAEF,MpE+7PR,MoE77PU,cAAA,eAEF,MpEg8PR,MoE97PU,eAAA,eAEF,MpEi8PR,MoE/7PU,aAAA,eAfF,KAAgC,QAAA,iBAChC,MpEs9PR,MoEp9PU,YAAA,iBAEF,MpEu9PR,MoEr9PU,cAAA,iBAEF,MpEw9PR,MoEt9PU,eAAA,iBAEF,MpEy9PR,MoEv9PU,aAAA,iBAfF,KAAgC,QAAA,eAChC,MpE8+PR,MoE5+PU,YAAA,eAEF,MpE++PR,MoE7+PU,cAAA,eAEF,MpEg/PR,MoE9+PU,eAAA,eAEF,MpEi/PR,MoE/+PU,aAAA,eAQF,MAAwB,OAAA,kBACxB,OpE++PR,OoE7+PU,WAAA,kBAEF,OpEg/PR,OoE9+PU,aAAA,kBAEF,OpEi/PR,OoE/+PU,cAAA,kBAEF,OpEk/PR,OoEh/PU,YAAA,kBAfF,MAAwB,OAAA,iBACxB,OpEugQR,OoErgQU,WAAA,iBAEF,OpEwgQR,OoEtgQU,aAAA,iBAEF,OpEygQR,OoEvgQU,cAAA,iBAEF,OpE0gQR,OoExgQU,YAAA,iBAfF,MAAwB,OAAA,gBACxB,OpE+hQR,OoE7hQU,WAAA,gBAEF,OpEgiQR,OoE9hQU,aAAA,gBAEF,OpEiiQR,OoE/hQU,cAAA,gBAEF,OpEkiQR,OoEhiQU,YAAA,gBAfF,MAAwB,OAAA,kBACxB,OpEujQR,OoErjQU,WAAA,kBAEF,OpEwjQR,OoEtjQU,aAAA,kBAEF,OpEyjQR,OoEvjQU,cAAA,kBAEF,OpE0jQR,OoExjQU,YAAA,kBAfF,MAAwB,OAAA,gBACxB,OpE+kQR,OoE7kQU,WAAA,gBAEF,OpEglQR,OoE9kQU,aAAA,gBAEF,OpEilQR,OoE/kQU,cAAA,gBAEF,OpEklQR,OoEhlQU,YAAA,gBAMN,QAAmB,OAAA,eACnB,SpEklQJ,SoEhlQM,WAAA,eAEF,SpEmlQJ,SoEjlQM,aAAA,eAEF,SpEolQJ,SoEllQM,cAAA,eAEF,SpEqlQJ,SoEnlQM,YAAA,exDTF,yBwDlDI,QAAgC,OAAA,YAChC,SpEspQN,SoEppQQ,WAAA,YAEF,SpEspQN,SoEppQQ,aAAA,YAEF,SpEspQN,SoEppQQ,cAAA,YAEF,SpEspQN,SoEppQQ,YAAA,YAfF,QAAgC,OAAA,iBAChC,SpEyqQN,SoEvqQQ,WAAA,iBAEF,SpEyqQN,SoEvqQQ,aAAA,iBAEF,SpEyqQN,SoEvqQQ,cAAA,iBAEF,SpEyqQN,SoEvqQQ,YAAA,iBAfF,QAAgC,OAAA,gBAChC,SpE4rQN,SoE1rQQ,WAAA,gBAEF,SpE4rQN,SoE1rQQ,aAAA,gBAEF,SpE4rQN,SoE1rQQ,cAAA,gBAEF,SpE4rQN,SoE1rQQ,YAAA,gBAfF,QAAgC,OAAA,eAChC,SpE+sQN,SoE7sQQ,WAAA,eAEF,SpE+sQN,SoE7sQQ,aAAA,eAEF,SpE+sQN,SoE7sQQ,cAAA,eAEF,SpE+sQN,SoE7sQQ,YAAA,eAfF,QAAgC,OAAA,iBAChC,SpEkuQN,SoEhuQQ,WAAA,iBAEF,SpEkuQN,SoEhuQQ,aAAA,iBAEF,SpEkuQN,SoEhuQQ,cAAA,iBAEF,SpEkuQN,SoEhuQQ,YAAA,iBAfF,QAAgC,OAAA,eAChC,SpEqvQN,SoEnvQQ,WAAA,eAEF,SpEqvQN,SoEnvQQ,aAAA,eAEF,SpEqvQN,SoEnvQQ,cAAA,eAEF,SpEqvQN,SoEnvQQ,YAAA,eAfF,QAAgC,QAAA,YAChC,SpEwwQN,SoEtwQQ,YAAA,YAEF,SpEwwQN,SoEtwQQ,cAAA,YAEF,SpEwwQN,SoEtwQQ,eAAA,YAEF,SpEwwQN,SoEtwQQ,aAAA,YAfF,QAAgC,QAAA,iBAChC,SpE2xQN,SoEzxQQ,YAAA,iBAEF,SpE2xQN,SoEzxQQ,cAAA,iBAEF,SpE2xQN,SoEzxQQ,eAAA,iBAEF,SpE2xQN,SoEzxQQ,aAAA,iBAfF,QAAgC,QAAA,gBAChC,SpE8yQN,SoE5yQQ,YAAA,gBAEF,SpE8yQN,SoE5yQQ,cAAA,gBAEF,SpE8yQN,SoE5yQQ,eAAA,gBAEF,SpE8yQN,SoE5yQQ,aAAA,gBAfF,QAAgC,QAAA,eAChC,SpEi0QN,SoE/zQQ,YAAA,eAEF,SpEi0QN,SoE/zQQ,cAAA,eAEF,SpEi0QN,SoE/zQQ,eAAA,eAEF,SpEi0QN,SoE/zQQ,aAAA,eAfF,QAAgC,QAAA,iBAChC,SpEo1QN,SoEl1QQ,YAAA,iBAEF,SpEo1QN,SoEl1QQ,cAAA,iBAEF,SpEo1QN,SoEl1QQ,eAAA,iBAEF,SpEo1QN,SoEl1QQ,aAAA,iBAfF,QAAgC,QAAA,eAChC,SpEu2QN,SoEr2QQ,YAAA,eAEF,SpEu2QN,SoEr2QQ,cAAA,eAEF,SpEu2QN,SoEr2QQ,eAAA,eAEF,SpEu2QN,SoEr2QQ,aAAA,eAQF,SAAwB,OAAA,kBACxB,UpEm2QN,UoEj2QQ,WAAA,kBAEF,UpEm2QN,UoEj2QQ,aAAA,kBAEF,UpEm2QN,UoEj2QQ,cAAA,kBAEF,UpEm2QN,UoEj2QQ,YAAA,kBAfF,SAAwB,OAAA,iBACxB,UpEs3QN,UoEp3QQ,WAAA,iBAEF,UpEs3QN,UoEp3QQ,aAAA,iBAEF,UpEs3QN,UoEp3QQ,cAAA,iBAEF,UpEs3QN,UoEp3QQ,YAAA,iBAfF,SAAwB,OAAA,gBACxB,UpEy4QN,UoEv4QQ,WAAA,gBAEF,UpEy4QN,UoEv4QQ,aAAA,gBAEF,UpEy4QN,UoEv4QQ,cAAA,gBAEF,UpEy4QN,UoEv4QQ,YAAA,gBAfF,SAAwB,OAAA,kBACxB,UpE45QN,UoE15QQ,WAAA,kBAEF,UpE45QN,UoE15QQ,aAAA,kBAEF,UpE45QN,UoE15QQ,cAAA,kBAEF,UpE45QN,UoE15QQ,YAAA,kBAfF,SAAwB,OAAA,gBACxB,UpE+6QN,UoE76QQ,WAAA,gBAEF,UpE+6QN,UoE76QQ,aAAA,gBAEF,UpE+6QN,UoE76QQ,cAAA,gBAEF,UpE+6QN,UoE76QQ,YAAA,gBAMN,WAAmB,OAAA,eACnB,YpE66QF,YoE36QI,WAAA,eAEF,YpE66QF,YoE36QI,aAAA,eAEF,YpE66QF,YoE36QI,cAAA,eAEF,YpE66QF,YoE36QI,YAAA,gBxDTF,yBwDlDI,QAAgC,OAAA,YAChC,SpE++QN,SoE7+QQ,WAAA,YAEF,SpE++QN,SoE7+QQ,aAAA,YAEF,SpE++QN,SoE7+QQ,cAAA,YAEF,SpE++QN,SoE7+QQ,YAAA,YAfF,QAAgC,OAAA,iBAChC,SpEkgRN,SoEhgRQ,WAAA,iBAEF,SpEkgRN,SoEhgRQ,aAAA,iBAEF,SpEkgRN,SoEhgRQ,cAAA,iBAEF,SpEkgRN,SoEhgRQ,YAAA,iBAfF,QAAgC,OAAA,gBAChC,SpEqhRN,SoEnhRQ,WAAA,gBAEF,SpEqhRN,SoEnhRQ,aAAA,gBAEF,SpEqhRN,SoEnhRQ,cAAA,gBAEF,SpEqhRN,SoEnhRQ,YAAA,gBAfF,QAAgC,OAAA,eAChC,SpEwiRN,SoEtiRQ,WAAA,eAEF,SpEwiRN,SoEtiRQ,aAAA,eAEF,SpEwiRN,SoEtiRQ,cAAA,eAEF,SpEwiRN,SoEtiRQ,YAAA,eAfF,QAAgC,OAAA,iBAChC,SpE2jRN,SoEzjRQ,WAAA,iBAEF,SpE2jRN,SoEzjRQ,aAAA,iBAEF,SpE2jRN,SoEzjRQ,cAAA,iBAEF,SpE2jRN,SoEzjRQ,YAAA,iBAfF,QAAgC,OAAA,eAChC,SpE8kRN,SoE5kRQ,WAAA,eAEF,SpE8kRN,SoE5kRQ,aAAA,eAEF,SpE8kRN,SoE5kRQ,cAAA,eAEF,SpE8kRN,SoE5kRQ,YAAA,eAfF,QAAgC,QAAA,YAChC,SpEimRN,SoE/lRQ,YAAA,YAEF,SpEimRN,SoE/lRQ,cAAA,YAEF,SpEimRN,SoE/lRQ,eAAA,YAEF,SpEimRN,SoE/lRQ,aAAA,YAfF,QAAgC,QAAA,iBAChC,SpEonRN,SoElnRQ,YAAA,iBAEF,SpEonRN,SoElnRQ,cAAA,iBAEF,SpEonRN,SoElnRQ,eAAA,iBAEF,SpEonRN,SoElnRQ,aAAA,iBAfF,QAAgC,QAAA,gBAChC,SpEuoRN,SoEroRQ,YAAA,gBAEF,SpEuoRN,SoEroRQ,cAAA,gBAEF,SpEuoRN,SoEroRQ,eAAA,gBAEF,SpEuoRN,SoEroRQ,aAAA,gBAfF,QAAgC,QAAA,eAChC,SpE0pRN,SoExpRQ,YAAA,eAEF,SpE0pRN,SoExpRQ,cAAA,eAEF,SpE0pRN,SoExpRQ,eAAA,eAEF,SpE0pRN,SoExpRQ,aAAA,eAfF,QAAgC,QAAA,iBAChC,SpE6qRN,SoE3qRQ,YAAA,iBAEF,SpE6qRN,SoE3qRQ,cAAA,iBAEF,SpE6qRN,SoE3qRQ,eAAA,iBAEF,SpE6qRN,SoE3qRQ,aAAA,iBAfF,QAAgC,QAAA,eAChC,SpEgsRN,SoE9rRQ,YAAA,eAEF,SpEgsRN,SoE9rRQ,cAAA,eAEF,SpEgsRN,SoE9rRQ,eAAA,eAEF,SpEgsRN,SoE9rRQ,aAAA,eAQF,SAAwB,OAAA,kBACxB,UpE4rRN,UoE1rRQ,WAAA,kBAEF,UpE4rRN,UoE1rRQ,aAAA,kBAEF,UpE4rRN,UoE1rRQ,cAAA,kBAEF,UpE4rRN,UoE1rRQ,YAAA,kBAfF,SAAwB,OAAA,iBACxB,UpE+sRN,UoE7sRQ,WAAA,iBAEF,UpE+sRN,UoE7sRQ,aAAA,iBAEF,UpE+sRN,UoE7sRQ,cAAA,iBAEF,UpE+sRN,UoE7sRQ,YAAA,iBAfF,SAAwB,OAAA,gBACxB,UpEkuRN,UoEhuRQ,WAAA,gBAEF,UpEkuRN,UoEhuRQ,aAAA,gBAEF,UpEkuRN,UoEhuRQ,cAAA,gBAEF,UpEkuRN,UoEhuRQ,YAAA,gBAfF,SAAwB,OAAA,kBACxB,UpEqvRN,UoEnvRQ,WAAA,kBAEF,UpEqvRN,UoEnvRQ,aAAA,kBAEF,UpEqvRN,UoEnvRQ,cAAA,kBAEF,UpEqvRN,UoEnvRQ,YAAA,kBAfF,SAAwB,OAAA,gBACxB,UpEwwRN,UoEtwRQ,WAAA,gBAEF,UpEwwRN,UoEtwRQ,aAAA,gBAEF,UpEwwRN,UoEtwRQ,cAAA,gBAEF,UpEwwRN,UoEtwRQ,YAAA,gBAMN,WAAmB,OAAA,eACnB,YpEswRF,YoEpwRI,WAAA,eAEF,YpEswRF,YoEpwRI,aAAA,eAEF,YpEswRF,YoEpwRI,cAAA,eAEF,YpEswRF,YoEpwRI,YAAA,gBxDTF,yBwDlDI,QAAgC,OAAA,YAChC,SpEw0RN,SoEt0RQ,WAAA,YAEF,SpEw0RN,SoEt0RQ,aAAA,YAEF,SpEw0RN,SoEt0RQ,cAAA,YAEF,SpEw0RN,SoEt0RQ,YAAA,YAfF,QAAgC,OAAA,iBAChC,SpE21RN,SoEz1RQ,WAAA,iBAEF,SpE21RN,SoEz1RQ,aAAA,iBAEF,SpE21RN,SoEz1RQ,cAAA,iBAEF,SpE21RN,SoEz1RQ,YAAA,iBAfF,QAAgC,OAAA,gBAChC,SpE82RN,SoE52RQ,WAAA,gBAEF,SpE82RN,SoE52RQ,aAAA,gBAEF,SpE82RN,SoE52RQ,cAAA,gBAEF,SpE82RN,SoE52RQ,YAAA,gBAfF,QAAgC,OAAA,eAChC,SpEi4RN,SoE/3RQ,WAAA,eAEF,SpEi4RN,SoE/3RQ,aAAA,eAEF,SpEi4RN,SoE/3RQ,cAAA,eAEF,SpEi4RN,SoE/3RQ,YAAA,eAfF,QAAgC,OAAA,iBAChC,SpEo5RN,SoEl5RQ,WAAA,iBAEF,SpEo5RN,SoEl5RQ,aAAA,iBAEF,SpEo5RN,SoEl5RQ,cAAA,iBAEF,SpEo5RN,SoEl5RQ,YAAA,iBAfF,QAAgC,OAAA,eAChC,SpEu6RN,SoEr6RQ,WAAA,eAEF,SpEu6RN,SoEr6RQ,aAAA,eAEF,SpEu6RN,SoEr6RQ,cAAA,eAEF,SpEu6RN,SoEr6RQ,YAAA,eAfF,QAAgC,QAAA,YAChC,SpE07RN,SoEx7RQ,YAAA,YAEF,SpE07RN,SoEx7RQ,cAAA,YAEF,SpE07RN,SoEx7RQ,eAAA,YAEF,SpE07RN,SoEx7RQ,aAAA,YAfF,QAAgC,QAAA,iBAChC,SpE68RN,SoE38RQ,YAAA,iBAEF,SpE68RN,SoE38RQ,cAAA,iBAEF,SpE68RN,SoE38RQ,eAAA,iBAEF,SpE68RN,SoE38RQ,aAAA,iBAfF,QAAgC,QAAA,gBAChC,SpEg+RN,SoE99RQ,YAAA,gBAEF,SpEg+RN,SoE99RQ,cAAA,gBAEF,SpEg+RN,SoE99RQ,eAAA,gBAEF,SpEg+RN,SoE99RQ,aAAA,gBAfF,QAAgC,QAAA,eAChC,SpEm/RN,SoEj/RQ,YAAA,eAEF,SpEm/RN,SoEj/RQ,cAAA,eAEF,SpEm/RN,SoEj/RQ,eAAA,eAEF,SpEm/RN,SoEj/RQ,aAAA,eAfF,QAAgC,QAAA,iBAChC,SpEsgSN,SoEpgSQ,YAAA,iBAEF,SpEsgSN,SoEpgSQ,cAAA,iBAEF,SpEsgSN,SoEpgSQ,eAAA,iBAEF,SpEsgSN,SoEpgSQ,aAAA,iBAfF,QAAgC,QAAA,eAChC,SpEyhSN,SoEvhSQ,YAAA,eAEF,SpEyhSN,SoEvhSQ,cAAA,eAEF,SpEyhSN,SoEvhSQ,eAAA,eAEF,SpEyhSN,SoEvhSQ,aAAA,eAQF,SAAwB,OAAA,kBACxB,UpEqhSN,UoEnhSQ,WAAA,kBAEF,UpEqhSN,UoEnhSQ,aAAA,kBAEF,UpEqhSN,UoEnhSQ,cAAA,kBAEF,UpEqhSN,UoEnhSQ,YAAA,kBAfF,SAAwB,OAAA,iBACxB,UpEwiSN,UoEtiSQ,WAAA,iBAEF,UpEwiSN,UoEtiSQ,aAAA,iBAEF,UpEwiSN,UoEtiSQ,cAAA,iBAEF,UpEwiSN,UoEtiSQ,YAAA,iBAfF,SAAwB,OAAA,gBACxB,UpE2jSN,UoEzjSQ,WAAA,gBAEF,UpE2jSN,UoEzjSQ,aAAA,gBAEF,UpE2jSN,UoEzjSQ,cAAA,gBAEF,UpE2jSN,UoEzjSQ,YAAA,gBAfF,SAAwB,OAAA,kBACxB,UpE8kSN,UoE5kSQ,WAAA,kBAEF,UpE8kSN,UoE5kSQ,aAAA,kBAEF,UpE8kSN,UoE5kSQ,cAAA,kBAEF,UpE8kSN,UoE5kSQ,YAAA,kBAfF,SAAwB,OAAA,gBACxB,UpEimSN,UoE/lSQ,WAAA,gBAEF,UpEimSN,UoE/lSQ,aAAA,gBAEF,UpEimSN,UoE/lSQ,cAAA,gBAEF,UpEimSN,UoE/lSQ,YAAA,gBAMN,WAAmB,OAAA,eACnB,YpE+lSF,YoE7lSI,WAAA,eAEF,YpE+lSF,YoE7lSI,aAAA,eAEF,YpE+lSF,YoE7lSI,cAAA,eAEF,YpE+lSF,YoE7lSI,YAAA,gBxDTF,0BwDlDI,QAAgC,OAAA,YAChC,SpEiqSN,SoE/pSQ,WAAA,YAEF,SpEiqSN,SoE/pSQ,aAAA,YAEF,SpEiqSN,SoE/pSQ,cAAA,YAEF,SpEiqSN,SoE/pSQ,YAAA,YAfF,QAAgC,OAAA,iBAChC,SpEorSN,SoElrSQ,WAAA,iBAEF,SpEorSN,SoElrSQ,aAAA,iBAEF,SpEorSN,SoElrSQ,cAAA,iBAEF,SpEorSN,SoElrSQ,YAAA,iBAfF,QAAgC,OAAA,gBAChC,SpEusSN,SoErsSQ,WAAA,gBAEF,SpEusSN,SoErsSQ,aAAA,gBAEF,SpEusSN,SoErsSQ,cAAA,gBAEF,SpEusSN,SoErsSQ,YAAA,gBAfF,QAAgC,OAAA,eAChC,SpE0tSN,SoExtSQ,WAAA,eAEF,SpE0tSN,SoExtSQ,aAAA,eAEF,SpE0tSN,SoExtSQ,cAAA,eAEF,SpE0tSN,SoExtSQ,YAAA,eAfF,QAAgC,OAAA,iBAChC,SpE6uSN,SoE3uSQ,WAAA,iBAEF,SpE6uSN,SoE3uSQ,aAAA,iBAEF,SpE6uSN,SoE3uSQ,cAAA,iBAEF,SpE6uSN,SoE3uSQ,YAAA,iBAfF,QAAgC,OAAA,eAChC,SpEgwSN,SoE9vSQ,WAAA,eAEF,SpEgwSN,SoE9vSQ,aAAA,eAEF,SpEgwSN,SoE9vSQ,cAAA,eAEF,SpEgwSN,SoE9vSQ,YAAA,eAfF,QAAgC,QAAA,YAChC,SpEmxSN,SoEjxSQ,YAAA,YAEF,SpEmxSN,SoEjxSQ,cAAA,YAEF,SpEmxSN,SoEjxSQ,eAAA,YAEF,SpEmxSN,SoEjxSQ,aAAA,YAfF,QAAgC,QAAA,iBAChC,SpEsySN,SoEpySQ,YAAA,iBAEF,SpEsySN,SoEpySQ,cAAA,iBAEF,SpEsySN,SoEpySQ,eAAA,iBAEF,SpEsySN,SoEpySQ,aAAA,iBAfF,QAAgC,QAAA,gBAChC,SpEyzSN,SoEvzSQ,YAAA,gBAEF,SpEyzSN,SoEvzSQ,cAAA,gBAEF,SpEyzSN,SoEvzSQ,eAAA,gBAEF,SpEyzSN,SoEvzSQ,aAAA,gBAfF,QAAgC,QAAA,eAChC,SpE40SN,SoE10SQ,YAAA,eAEF,SpE40SN,SoE10SQ,cAAA,eAEF,SpE40SN,SoE10SQ,eAAA,eAEF,SpE40SN,SoE10SQ,aAAA,eAfF,QAAgC,QAAA,iBAChC,SpE+1SN,SoE71SQ,YAAA,iBAEF,SpE+1SN,SoE71SQ,cAAA,iBAEF,SpE+1SN,SoE71SQ,eAAA,iBAEF,SpE+1SN,SoE71SQ,aAAA,iBAfF,QAAgC,QAAA,eAChC,SpEk3SN,SoEh3SQ,YAAA,eAEF,SpEk3SN,SoEh3SQ,cAAA,eAEF,SpEk3SN,SoEh3SQ,eAAA,eAEF,SpEk3SN,SoEh3SQ,aAAA,eAQF,SAAwB,OAAA,kBACxB,UpE82SN,UoE52SQ,WAAA,kBAEF,UpE82SN,UoE52SQ,aAAA,kBAEF,UpE82SN,UoE52SQ,cAAA,kBAEF,UpE82SN,UoE52SQ,YAAA,kBAfF,SAAwB,OAAA,iBACxB,UpEi4SN,UoE/3SQ,WAAA,iBAEF,UpEi4SN,UoE/3SQ,aAAA,iBAEF,UpEi4SN,UoE/3SQ,cAAA,iBAEF,UpEi4SN,UoE/3SQ,YAAA,iBAfF,SAAwB,OAAA,gBACxB,UpEo5SN,UoEl5SQ,WAAA,gBAEF,UpEo5SN,UoEl5SQ,aAAA,gBAEF,UpEo5SN,UoEl5SQ,cAAA,gBAEF,UpEo5SN,UoEl5SQ,YAAA,gBAfF,SAAwB,OAAA,kBACxB,UpEu6SN,UoEr6SQ,WAAA,kBAEF,UpEu6SN,UoEr6SQ,aAAA,kBAEF,UpEu6SN,UoEr6SQ,cAAA,kBAEF,UpEu6SN,UoEr6SQ,YAAA,kBAfF,SAAwB,OAAA,gBACxB,UpE07SN,UoEx7SQ,WAAA,gBAEF,UpE07SN,UoEx7SQ,aAAA,gBAEF,UpE07SN,UoEx7SQ,cAAA,gBAEF,UpE07SN,UoEx7SQ,YAAA,gBAMN,WAAmB,OAAA,eACnB,YpEw7SF,YoEt7SI,WAAA,eAEF,YpEw7SF,YoEt7SI,aAAA,eAEF,YpEw7SF,YoEt7SI,cAAA,eAEF,YpEw7SF,YoEt7SI,YAAA,gBC/DN,gBAAkB,YAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,oBAIlB,cAAiB,WAAA,kBACjB,WAAiB,YAAA,iBACjB,aAAiB,YAAA,iBACjB,eCTE,SAAA,OACA,cAAA,SACA,YAAA,ODeE,WAAwB,WAAA,eACxB,YAAwB,WAAA,gBACxB,aAAwB,WAAA,iBzDqCxB,yByDvCA,cAAwB,WAAA,eACxB,eAAwB,WAAA,gBACxB,gBAAwB,WAAA,kBzDqCxB,yByDvCA,cAAwB,WAAA,eACxB,eAAwB,WAAA,gBACxB,gBAAwB,WAAA,kBzDqCxB,yByDvCA,cAAwB,WAAA,eACxB,eAAwB,WAAA,gBACxB,gBAAwB,WAAA,kBzDqCxB,0ByDvCA,cAAwB,WAAA,eACxB,eAAwB,WAAA,gBACxB,gBAAwB,WAAA,kBAM5B,gBAAmB,eAAA,oBACnB,gBAAmB,eAAA,oBACnB,iBAAmB,eAAA,qBAInB,mBAAuB,YAAA,cACvB,qBAAuB,YAAA,kBACvB,oBAAuB,YAAA,cACvB,kBAAuB,YAAA,cACvB,oBAAuB,YAAA,iBACvB,aAAuB,WAAA,iBAIvB,YAAc,MAAA,eEvCZ,cACE,MAAA,kBpEUF,qBAAA,qBoELM,MAAA,kBANN,gBACE,MAAA,kBpEUF,uBAAA,uBoELM,MAAA,kBANN,cACE,MAAA,kBpEUF,qBAAA,qBoELM,MAAA,kBANN,WACE,MAAA,kBpEUF,kBAAA,kBoELM,MAAA,kBANN,cACE,MAAA,kBpEUF,qBAAA,qBoELM,MAAA,kBANN,aACE,MAAA,kBpEUF,oBAAA,oBoELM,MAAA,kBANN,YACE,MAAA,kBpEUF,mBAAA,mBoELM,MAAA,kBANN,WACE,MAAA,kBpEUF,kBAAA,kBoELM,MAAA,kBFuCR,WAAa,MAAA,kBACb,YAAc,MAAA,kBAEd,eAAiB,MAAA,yBACjB,eAAiB,MAAA,+BAIjB,WGvDE,KAAA,CAAA,CAAA,EAAA,EACA,MAAA,YACA,YAAA,KACA,iBAAA,YACA,OAAA,EHuDF,sBAAwB,gBAAA,eAExB,YACE,WAAA,qBACA,cAAA,qBAKF,YAAc,MAAA,kBIjEd,SACE,WAAA,kBAGF,WACE,WAAA,iBCAA,a3EOF,ECwtTE,QADA,S0ExtTI,YAAA,eAEA,WAAA,eAGF,YAEI,gBAAA,UASJ,mBACE,QAAA,KAAA,YAAA,I3E+LN,I2EhLM,YAAA,mB1EusTJ,W0ErsTE,IAEE,OAAA,IAAA,MAAA,QACA,kBAAA,MAQF,MACE,QAAA,mB1EisTJ,I0E9rTE,GAEE,kBAAA,M1EgsTJ,GACA,G0E9rTE,EAGE,QAAA,EACA,OAAA,EAGF,G1E4rTF,G0E1rTI,iBAAA,MAQF,MACE,KAAA,G3E5CN,K2E+CM,UAAA,gBhEvFJ,WgE0FI,UAAA,gB5C9EN,Q4CmFM,QAAA,KvC/FN,OuCkGM,OAAA,IAAA,MAAA,K5DnGN,O4DuGM,gBAAA,mBADF,U1EsrTF,U0EjrTM,iBAAA,e1EqrTN,mBcxvTF,mB4D0EQ,OAAA,IAAA,MAAA,kB5DWR,Y4DNM,MAAA,Q1EkrTJ,wBAFA,eetyTA,efuyTA,qB0E3qTM,aAAA,Q5DlBR,sB4DuBM,MAAA,QACA,aAAA","sourcesContent":["/*!\n * Bootstrap v4.3.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"root\";\n@import \"reboot\";\n@import \"type\";\n@import \"images\";\n@import \"code\";\n@import \"grid\";\n@import \"tables\";\n@import \"forms\";\n@import \"buttons\";\n@import \"transitions\";\n@import \"dropdown\";\n@import \"button-group\";\n@import \"input-group\";\n@import \"custom-forms\";\n@import \"nav\";\n@import \"navbar\";\n@import \"card\";\n@import \"breadcrumb\";\n@import \"pagination\";\n@import \"badge\";\n@import \"jumbotron\";\n@import \"alert\";\n@import \"progress\";\n@import \"media\";\n@import \"list-group\";\n@import \"close\";\n@import \"toasts\";\n@import \"modal\";\n@import \"tooltip\";\n@import \"popover\";\n@import \"carousel\";\n@import \"spinners\";\n@import \"utilities\";\n@import \"print\";\n",":root {\n // Custom variable values only support SassScript inside `#{}`.\n @each $color, $value in $colors {\n --#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$color}: #{$value};\n }\n\n @each $bp, $value in $grid-breakpoints {\n --breakpoint-#{$bp}: #{$value};\n }\n\n // Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --font-family-sans-serif: #{inspect($font-family-sans-serif)};\n --font-family-monospace: #{inspect($font-family-monospace)};\n}\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\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// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -webkit-tap-highlight-color: rgba($black, 0); // 5\n}\n\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\n// TODO: remove in v5\n// stylelint-disable-next-line selector-list-comma-newline-after\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\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. Set an explicit initial text-align value so that we can later use\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n @include font-size($font-size-base);\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\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// stylelint-disable-next-line selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\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`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Duplicate behavior to the data-* attribute for our tooltip plugin\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Remove the bottom border in Firefox 39-.\n// 5. Prevent the text-decoration to be skipped.\n\nabbr[title],\nabbr[data-original-title] { // 1\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 4\n text-decoration-skip-ink: none; // 5\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\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\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\nb,\nstrong {\n font-weight: $font-weight-bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n\nsmall {\n @include font-size(80%); // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n @include font-size(75%);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\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([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n // stylelint-disable-next-line property-blacklist\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// Remove the inheritance of word-wrap in Safari.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24990\nselect {\n word-wrap: normal;\n}\n\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\n[type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Opinionated: add \"hand\" cursor to non-disabled button elements.\n@if $enable-pointer-cursor-for-buttons {\n button,\n [type=\"button\"],\n [type=\"reset\"],\n [type=\"submit\"] {\n &:not(:disabled) {\n cursor: pointer;\n }\n }\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n @include font-size(1.5rem);\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap v4.3.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n -webkit-text-decoration-skip-ink: none;\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,\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: .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: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\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,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-weight: 500;\n line-height: 1.2;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014\\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n}\n\n.col-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n -ms-flex-order: -1;\n order: -1;\n}\n\n.order-last {\n -ms-flex-order: 13;\n order: 13;\n}\n\n.order-0 {\n -ms-flex-order: 0;\n order: 0;\n}\n\n.order-1 {\n -ms-flex-order: 1;\n order: 1;\n}\n\n.order-2 {\n -ms-flex-order: 2;\n order: 2;\n}\n\n.order-3 {\n -ms-flex-order: 3;\n order: 3;\n}\n\n.order-4 {\n -ms-flex-order: 4;\n order: 4;\n}\n\n.order-5 {\n -ms-flex-order: 5;\n order: 5;\n}\n\n.order-6 {\n -ms-flex-order: 6;\n order: 6;\n}\n\n.order-7 {\n -ms-flex-order: 7;\n order: 7;\n}\n\n.order-8 {\n -ms-flex-order: 8;\n order: 8;\n}\n\n.order-9 {\n -ms-flex-order: 9;\n order: 9;\n}\n\n.order-10 {\n -ms-flex-order: 10;\n order: 10;\n}\n\n.order-11 {\n -ms-flex-order: 11;\n order: 11;\n}\n\n.order-12 {\n -ms-flex-order: 12;\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-sm-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-sm-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-sm-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-sm-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-sm-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-sm-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-sm-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-sm-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-sm-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-sm-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-sm-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-sm-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-sm-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-sm-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-sm-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-md-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-md-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-md-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-md-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-md-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-md-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-md-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-md-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-md-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-md-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-md-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-md-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-md-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-md-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-md-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-lg-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-lg-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-lg-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-lg-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-lg-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-lg-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-lg-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-lg-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-lg-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-lg-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-lg-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-lg-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-lg-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-lg-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-lg-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-xl-1 {\n -ms-flex: 0 0 8.333333%;\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n -ms-flex: 0 0 16.666667%;\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n -ms-flex: 0 0 25%;\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n -ms-flex: 0 0 33.333333%;\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n -ms-flex: 0 0 41.666667%;\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n -ms-flex: 0 0 50%;\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n -ms-flex: 0 0 58.333333%;\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n -ms-flex: 0 0 66.666667%;\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n -ms-flex: 0 0 75%;\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n -ms-flex: 0 0 83.333333%;\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n -ms-flex: 0 0 91.666667%;\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n -ms-flex: 0 0 100%;\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n -ms-flex-order: -1;\n order: -1;\n }\n .order-xl-last {\n -ms-flex-order: 13;\n order: 13;\n }\n .order-xl-0 {\n -ms-flex-order: 0;\n order: 0;\n }\n .order-xl-1 {\n -ms-flex-order: 1;\n order: 1;\n }\n .order-xl-2 {\n -ms-flex-order: 2;\n order: 2;\n }\n .order-xl-3 {\n -ms-flex-order: 3;\n order: 3;\n }\n .order-xl-4 {\n -ms-flex-order: 4;\n order: 4;\n }\n .order-xl-5 {\n -ms-flex-order: 5;\n order: 5;\n }\n .order-xl-6 {\n -ms-flex-order: 6;\n order: 6;\n }\n .order-xl-7 {\n -ms-flex-order: 7;\n order: 7;\n }\n .order-xl-8 {\n -ms-flex-order: 8;\n order: 8;\n }\n .order-xl-9 {\n -ms-flex-order: 9;\n order: 9;\n }\n .order-xl-10 {\n -ms-flex-order: 10;\n order: 10;\n }\n .order-xl-11 {\n -ms-flex-order: 11;\n order: 11;\n }\n .order-xl-12 {\n -ms-flex-order: 12;\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n margin-bottom: 1rem;\n color: #212529;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n color: #212529;\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-primary th,\n.table-primary td,\n.table-primary thead th,\n.table-primary tbody + tbody {\n border-color: #7abaff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-secondary th,\n.table-secondary td,\n.table-secondary thead th,\n.table-secondary tbody + tbody {\n border-color: #b3b7bb;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-success th,\n.table-success td,\n.table-success thead th,\n.table-success tbody + tbody {\n border-color: #8fd19e;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-info th,\n.table-info td,\n.table-info thead th,\n.table-info tbody + tbody {\n border-color: #86cfda;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-warning th,\n.table-warning td,\n.table-warning thead th,\n.table-warning tbody + tbody {\n border-color: #ffdf7e;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-danger th,\n.table-danger td,\n.table-danger thead th,\n.table-danger tbody + tbody {\n border-color: #ed969e;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-light th,\n.table-light td,\n.table-light thead th,\n.table-light tbody + tbody {\n border-color: #fbfcfc;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th,\n.table-dark tbody + tbody {\n border-color: #95999c;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #343a40;\n border-color: #454d55;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #454d55;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n color: #fff;\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::-webkit-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-moz-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::-ms-input-placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm {\n height: calc(1.5em + 0.5rem + 2px);\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.form-control-lg {\n height: calc(1.5em + 1rem + 2px);\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control[size], select.form-control[multiple] {\n height: auto;\n}\n\ntextarea.form-control {\n height: auto;\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: -ms-inline-flexbox;\n display: inline-flex;\n -ms-flex-align: center;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: .1rem;\n font-size: 0.875rem;\n line-height: 1.5;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n border-color: #28a745;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: center right calc(0.375em + 0.1875rem);\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:valid, .custom-select.is-valid {\n border-color: #28a745;\n padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem);\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px, url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-select:valid ~ .valid-feedback,\n.was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:valid ~ .valid-feedback,\n.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback,\n.form-control-file.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n border-color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n border-color: #34ce57;\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: .1rem;\n font-size: 0.875rem;\n line-height: 1.5;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n border-color: #dc3545;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E\");\n background-repeat: no-repeat;\n background-position: center right calc(0.375em + 0.1875rem);\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:invalid, .custom-select.is-invalid {\n border-color: #dc3545;\n padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem);\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px, url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E\") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-select:invalid ~ .invalid-feedback,\n.was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:invalid ~ .invalid-feedback,\n.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback,\n.form-control-file.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n border-color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n border-color: #e4606d;\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n -ms-flex-align: center;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n -ms-flex-negative: 0;\n flex-shrink: 0;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n color: #212529;\n text-align: center;\n vertical-align: middle;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n background-color: transparent;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover {\n color: #212529;\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n text-decoration: none;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle {\n white-space: nowrap;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-left {\n right: auto;\n left: 0;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n@media (min-width: 576px) {\n .dropdown-menu-sm-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-sm-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 768px) {\n .dropdown-menu-md-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-md-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 992px) {\n .dropdown-menu-lg-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-lg-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 1200px) {\n .dropdown-menu-xl-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-xl-right {\n right: 0;\n left: auto;\n }\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n pointer-events: none;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: -ms-inline-flexbox;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-toolbar {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) {\n margin-left: -1px;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-align: start;\n align-items: flex-start;\n -ms-flex-pack: center;\n justify-content: center;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n margin-top: -1px;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: stretch;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .form-control-plaintext,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .form-control-plaintext + .form-control,\n.input-group > .form-control-plaintext + .custom-select,\n.input-group > .form-control-plaintext + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {\n z-index: 3;\n}\n\n.input-group > .custom-file .custom-file-input:focus {\n z-index: 4;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: -ms-flexbox;\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn:focus,\n.input-group-append .btn:focus {\n z-index: 3;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group-lg > .form-control:not(textarea),\n.input-group-lg > .custom-select {\n height: calc(1.5em + 1rem + 2px);\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .custom-select,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.input-group-sm > .form-control:not(textarea),\n.input-group-sm > .custom-select {\n height: calc(1.5em + 0.5rem + 2px);\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .custom-select,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.input-group-lg > .custom-select,\n.input-group-sm > .custom-select {\n padding-right: 1.75rem;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: -ms-inline-flexbox;\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n border-color: #007bff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #80bdff;\n}\n\n.custom-control-input:not(:disabled):active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n border-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n position: relative;\n margin-bottom: 0;\n vertical-align: top;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n background-color: #fff;\n border: #adb5bd solid 1px;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background: no-repeat 50% / 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n border-color: #007bff;\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-switch {\n padding-left: 2.25rem;\n}\n\n.custom-switch .custom-control-label::before {\n left: -2.25rem;\n width: 1.75rem;\n pointer-events: all;\n border-radius: 0.5rem;\n}\n\n.custom-switch .custom-control-label::after {\n top: calc(0.25rem + 2px);\n left: calc(-2.25rem + 2px);\n width: calc(1rem - 4px);\n height: calc(1rem - 4px);\n background-color: #adb5bd;\n border-radius: 0.5rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-switch .custom-control-label::after {\n transition: none;\n }\n}\n\n.custom-switch .custom-control-input:checked ~ .custom-control-label::after {\n background-color: #fff;\n -webkit-transform: translateX(0.75rem);\n transform: translateX(0.75rem);\n}\n\n.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n display: none;\n}\n\n.custom-select-sm {\n height: calc(1.5em + 0.5rem + 2px);\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n padding-left: 0.5rem;\n font-size: 0.875rem;\n}\n\n.custom-select-lg {\n height: calc(1.5em + 1rem + 2px);\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n font-size: 1.25rem;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:disabled ~ .custom-file-label {\n background-color: #e9ecef;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-input ~ .custom-file-label[data-browse]::after {\n content: attr(data-browse);\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 0.75rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: calc(1.5em + 0.75rem);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: inherit;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n height: calc(1rem + 0.4rem);\n padding: 0;\n background-color: transparent;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range:focus::-webkit-slider-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-moz-range-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-ms-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n -webkit-appearance: none;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-webkit-slider-thumb {\n transition: none;\n }\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n -moz-appearance: none;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-moz-range-thumb {\n transition: none;\n }\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: 0;\n margin-right: 0.2rem;\n margin-left: 0.2rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-ms-thumb {\n transition: none;\n }\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range:disabled::-webkit-slider-thumb {\n background-color: #adb5bd;\n}\n\n.custom-range:disabled::-webkit-slider-runnable-track {\n cursor: default;\n}\n\n.custom-range:disabled::-moz-range-thumb {\n background-color: #adb5bd;\n}\n\n.custom-range:disabled::-moz-range-track {\n cursor: default;\n}\n\n.custom-range:disabled::-ms-thumb {\n background-color: #adb5bd;\n}\n\n.custom-control-label::before,\n.custom-file-label,\n.custom-select {\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-control-label::before,\n .custom-file-label,\n .custom-select {\n transition: none;\n }\n}\n\n.nav {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n pointer-events: none;\n cursor: default;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n -ms-flex-positive: 1;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: justify;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n -ms-flex-preferred-size: 100%;\n flex-basis: 100%;\n -ms-flex-positive: 1;\n flex-grow: 1;\n -ms-flex-align: center;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n -ms-flex-flow: row nowrap;\n flex-flow: row nowrap;\n -ms-flex-pack: start;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n -ms-flex-direction: row;\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n -ms-flex-wrap: nowrap;\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: -ms-flexbox !important;\n display: flex !important;\n -ms-flex-preferred-size: auto;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n -ms-flex-direction: column;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n -ms-flex-flow: row wrap;\n flex-flow: row wrap;\n }\n .card-group > .card {\n -ms-flex: 1 0 0%;\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-top,\n .card-group > .card:not(:last-child) .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-bottom,\n .card-group > .card:not(:last-child) .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-top,\n .card-group > .card:not(:first-child) .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-bottom,\n .card-group > .card:not(:first-child) .card-footer {\n border-bottom-left-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n -webkit-column-count: 3;\n -moz-column-count: 3;\n column-count: 3;\n -webkit-column-gap: 1.25rem;\n -moz-column-gap: 1.25rem;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion > .card {\n overflow: hidden;\n}\n\n.accordion > .card:not(:first-of-type) .card-header:first-child {\n border-radius: 0;\n}\n\n.accordion > .card:not(:first-of-type):not(:last-of-type) {\n border-bottom: 0;\n border-radius: 0;\n}\n\n.accordion > .card:first-of-type {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion > .card:last-of-type {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.accordion > .card .card-header {\n margin-bottom: -1px;\n}\n\n.breadcrumb {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-wrap: wrap;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: -ms-flexbox;\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .badge {\n transition: none;\n }\n}\n\na.badge:hover, a.badge:focus {\n text-decoration: none;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\na.badge-primary:hover, a.badge-primary:focus {\n color: #fff;\n background-color: #0062cc;\n}\n\na.badge-primary:focus, a.badge-primary.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\na.badge-secondary:hover, a.badge-secondary:focus {\n color: #fff;\n background-color: #545b62;\n}\n\na.badge-secondary:focus, a.badge-secondary.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\na.badge-success:hover, a.badge-success:focus {\n color: #fff;\n background-color: #1e7e34;\n}\n\na.badge-success:focus, a.badge-success.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\na.badge-info:hover, a.badge-info:focus {\n color: #fff;\n background-color: #117a8b;\n}\n\na.badge-info:focus, a.badge-info.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\na.badge-warning:hover, a.badge-warning:focus {\n color: #212529;\n background-color: #d39e00;\n}\n\na.badge-warning:focus, a.badge-warning.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\na.badge-danger:hover, a.badge-danger:focus {\n color: #fff;\n background-color: #bd2130;\n}\n\na.badge-danger:focus, a.badge-danger.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\na.badge-light:hover, a.badge-light:focus {\n color: #212529;\n background-color: #dae0e5;\n}\n\na.badge-light:focus, a.badge-light.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\na.badge-dark:hover, a.badge-dark:focus {\n color: #fff;\n background-color: #1d2124;\n}\n\na.badge-dark:focus, a.badge-dark.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@-webkit-keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: -ms-flexbox;\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-pack: center;\n justify-content: center;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n -webkit-animation: progress-bar-stripes 1s linear infinite;\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-bar-animated {\n -webkit-animation: none;\n animation: none;\n }\n}\n\n.media {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: start;\n align-items: flex-start;\n}\n\n.media-body {\n -ms-flex: 1;\n flex: 1;\n}\n\n.list-group {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n z-index: 1;\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n pointer-events: none;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-horizontal {\n -ms-flex-direction: row;\n flex-direction: row;\n}\n\n.list-group-horizontal .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n}\n\n.list-group-horizontal .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n}\n\n.list-group-horizontal .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n}\n\n@media (min-width: 576px) {\n .list-group-horizontal-sm {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .list-group-horizontal-sm .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-sm .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-sm .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n@media (min-width: 768px) {\n .list-group-horizontal-md {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .list-group-horizontal-md .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-md .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-md .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n@media (min-width: 992px) {\n .list-group-horizontal-lg {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .list-group-horizontal-lg .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-lg .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-lg .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .list-group-horizontal-xl {\n -ms-flex-direction: row;\n flex-direction: row;\n }\n .list-group-horizontal-xl .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-xl .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-xl .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush .list-group-item:last-child {\n margin-bottom: -1px;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n margin-bottom: 0;\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover {\n color: #000;\n text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {\n opacity: .75;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n\na.close.disabled {\n pointer-events: none;\n}\n\n.toast {\n max-width: 350px;\n overflow: hidden;\n font-size: 0.875rem;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.1);\n box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);\n -webkit-backdrop-filter: blur(10px);\n backdrop-filter: blur(10px);\n opacity: 0;\n border-radius: 0.25rem;\n}\n\n.toast:not(:last-child) {\n margin-bottom: 0.75rem;\n}\n\n.toast.showing {\n opacity: 1;\n}\n\n.toast.show {\n display: block;\n opacity: 1;\n}\n\n.toast.hide {\n display: none;\n}\n\n.toast-header {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n padding: 0.25rem 0.75rem;\n color: #6c757d;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n}\n\n.toast-body {\n padding: 0.75rem;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1050;\n display: none;\n width: 100%;\n height: 100%;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: -webkit-transform 0.3s ease-out;\n transition: transform 0.3s ease-out;\n transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;\n -webkit-transform: translate(0, -50px);\n transform: translate(0, -50px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n -webkit-transform: none;\n transform: none;\n}\n\n.modal-dialog-scrollable {\n display: -ms-flexbox;\n display: flex;\n max-height: calc(100% - 1rem);\n}\n\n.modal-dialog-scrollable .modal-content {\n max-height: calc(100vh - 1rem);\n overflow: hidden;\n}\n\n.modal-dialog-scrollable .modal-header,\n.modal-dialog-scrollable .modal-footer {\n -ms-flex-negative: 0;\n flex-shrink: 0;\n}\n\n.modal-dialog-scrollable .modal-body {\n overflow-y: auto;\n}\n\n.modal-dialog-centered {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n min-height: calc(100% - 1rem);\n}\n\n.modal-dialog-centered::before {\n display: block;\n height: calc(100vh - 1rem);\n content: \"\";\n}\n\n.modal-dialog-centered.modal-dialog-scrollable {\n -ms-flex-direction: column;\n flex-direction: column;\n -ms-flex-pack: center;\n justify-content: center;\n height: 100%;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable .modal-content {\n max-height: none;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable::before {\n content: none;\n}\n\n.modal-content {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1040;\n width: 100vw;\n height: 100vh;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: start;\n align-items: flex-start;\n -ms-flex-pack: justify;\n justify-content: space-between;\n padding: 1rem 1rem;\n border-bottom: 1px solid #dee2e6;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: end;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #dee2e6;\n border-bottom-right-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-scrollable {\n max-height: calc(100% - 3.5rem);\n }\n .modal-dialog-scrollable .modal-content {\n max-height: calc(100vh - 3.5rem);\n }\n .modal-dialog-centered {\n min-height: calc(100% - 3.5rem);\n }\n .modal-dialog-centered::before {\n height: calc(100vh - 3.5rem);\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg,\n .modal-xl {\n max-width: 800px;\n }\n}\n\n@media (min-width: 1200px) {\n .modal-xl {\n max-width: 1140px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top > .arrow, .bs-popover-auto[x-placement^=\"top\"] > .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^=\"top\"] > .arrow::before {\n bottom: 0;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^=\"top\"] > .arrow::after {\n bottom: 1px;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right > .arrow, .bs-popover-auto[x-placement^=\"right\"] > .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^=\"right\"] > .arrow::before {\n left: 0;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^=\"right\"] > .arrow::after {\n left: 1px;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::before {\n top: 0;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::after {\n top: 1px;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left > .arrow, .bs-popover-auto[x-placement^=\"left\"] > .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^=\"left\"] > .arrow::before {\n right: 0;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^=\"left\"] > .arrow::after {\n right: 1px;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel.pointer-event {\n -ms-touch-action: pan-y;\n touch-action: pan-y;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-inner::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.carousel-item {\n position: relative;\n display: none;\n float: left;\n width: 100%;\n margin-right: -100%;\n -webkit-backface-visibility: hidden;\n backface-visibility: hidden;\n transition: -webkit-transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out;\n transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next:not(.carousel-item-left),\n.active.carousel-item-right {\n -webkit-transform: translateX(100%);\n transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-right),\n.active.carousel-item-left {\n -webkit-transform: translateX(-100%);\n transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-property: opacity;\n -webkit-transform: none;\n transform: none;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n z-index: 1;\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n z-index: 0;\n opacity: 0;\n transition: 0s 0.6s opacity;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-right {\n transition: none;\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 1;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-align: center;\n align-items: center;\n -ms-flex-pack: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n transition: opacity 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-control-prev,\n .carousel-control-next {\n transition: none;\n }\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: 0.9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: no-repeat 50% / 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 15;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-pack: center;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n box-sizing: content-box;\n -ms-flex: 0 1 auto;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: #fff;\n background-clip: padding-box;\n border-top: 10px solid transparent;\n border-bottom: 10px solid transparent;\n opacity: .5;\n transition: opacity 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-indicators li {\n transition: none;\n }\n}\n\n.carousel-indicators .active {\n opacity: 1;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n@-webkit-keyframes spinner-border {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n\n@keyframes spinner-border {\n to {\n -webkit-transform: rotate(360deg);\n transform: rotate(360deg);\n }\n}\n\n.spinner-border {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: text-bottom;\n border: 0.25em solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n -webkit-animation: spinner-border .75s linear infinite;\n animation: spinner-border .75s linear infinite;\n}\n\n.spinner-border-sm {\n width: 1rem;\n height: 1rem;\n border-width: 0.2em;\n}\n\n@-webkit-keyframes spinner-grow {\n 0% {\n -webkit-transform: scale(0);\n transform: scale(0);\n }\n 50% {\n opacity: 1;\n }\n}\n\n@keyframes spinner-grow {\n 0% {\n -webkit-transform: scale(0);\n transform: scale(0);\n }\n 50% {\n opacity: 1;\n }\n}\n\n.spinner-grow {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: text-bottom;\n background-color: currentColor;\n border-radius: 50%;\n opacity: 0;\n -webkit-animation: spinner-grow .75s linear infinite;\n animation: spinner-grow .75s linear infinite;\n}\n\n.spinner-grow-sm {\n width: 1rem;\n height: 1rem;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded-sm {\n border-radius: 0.2rem !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-lg {\n border-radius: 0.3rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-pill {\n border-radius: 50rem !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\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-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: -ms-flexbox !important;\n display: flex !important;\n}\n\n.d-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n}\n\n@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\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-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: -ms-flexbox !important;\n display: flex !important;\n }\n .d-sm-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\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-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: -ms-flexbox !important;\n display: flex !important;\n }\n .d-md-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\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-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: -ms-flexbox !important;\n display: flex !important;\n }\n .d-lg-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\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-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: -ms-flexbox !important;\n display: flex !important;\n }\n .d-xl-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\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-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: -ms-flexbox !important;\n display: flex !important;\n }\n .d-print-inline-flex {\n display: -ms-inline-flexbox !important;\n display: inline-flex !important;\n }\n}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\n}\n\n.flex-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n}\n\n.flex-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n}\n\n.flex-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n}\n\n.flex-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n}\n\n.flex-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n}\n\n.flex-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n}\n\n.flex-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n}\n\n.flex-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n}\n\n.flex-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n}\n\n.flex-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n}\n\n.flex-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n}\n\n.flex-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n}\n\n.justify-content-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n}\n\n.justify-content-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n}\n\n.justify-content-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n}\n\n.justify-content-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n}\n\n.justify-content-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n}\n\n.align-items-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n}\n\n.align-items-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n}\n\n.align-items-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n}\n\n.align-items-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n}\n\n.align-items-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n}\n\n.align-content-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n}\n\n.align-content-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n}\n\n.align-content-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n}\n\n.align-content-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n}\n\n.align-content-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n}\n\n.align-content-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n}\n\n.align-self-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n}\n\n.align-self-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n}\n\n.align-self-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n}\n\n.align-self-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n}\n\n.align-self-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n}\n\n.align-self-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n}\n\n@media (min-width: 576px) {\n .flex-sm-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-sm-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-sm-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-sm-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-sm-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-sm-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-sm-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-sm-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-sm-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-sm-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-sm-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-sm-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-sm-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-sm-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-sm-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-sm-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-sm-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-sm-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-sm-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-sm-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-sm-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-sm-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-sm-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-sm-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-sm-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-sm-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-sm-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-sm-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-sm-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-sm-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-sm-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-sm-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-sm-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-sm-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 768px) {\n .flex-md-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-md-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-md-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-md-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-md-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-md-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-md-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-md-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-md-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-md-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-md-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-md-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-md-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-md-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-md-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-md-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-md-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-md-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-md-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-md-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-md-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-md-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-md-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-md-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-md-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-md-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-md-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-md-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-md-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-md-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-md-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-md-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-md-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-md-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 992px) {\n .flex-lg-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-lg-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-lg-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-lg-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-lg-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-lg-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-lg-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-lg-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-lg-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-lg-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-lg-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-lg-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-lg-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-lg-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-lg-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-lg-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-lg-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-lg-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-lg-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-lg-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-lg-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-lg-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-lg-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-lg-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-lg-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-lg-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-lg-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-lg-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-lg-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-lg-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-lg-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-lg-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-lg-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-lg-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n@media (min-width: 1200px) {\n .flex-xl-row {\n -ms-flex-direction: row !important;\n flex-direction: row !important;\n }\n .flex-xl-column {\n -ms-flex-direction: column !important;\n flex-direction: column !important;\n }\n .flex-xl-row-reverse {\n -ms-flex-direction: row-reverse !important;\n flex-direction: row-reverse !important;\n }\n .flex-xl-column-reverse {\n -ms-flex-direction: column-reverse !important;\n flex-direction: column-reverse !important;\n }\n .flex-xl-wrap {\n -ms-flex-wrap: wrap !important;\n flex-wrap: wrap !important;\n }\n .flex-xl-nowrap {\n -ms-flex-wrap: nowrap !important;\n flex-wrap: nowrap !important;\n }\n .flex-xl-wrap-reverse {\n -ms-flex-wrap: wrap-reverse !important;\n flex-wrap: wrap-reverse !important;\n }\n .flex-xl-fill {\n -ms-flex: 1 1 auto !important;\n flex: 1 1 auto !important;\n }\n .flex-xl-grow-0 {\n -ms-flex-positive: 0 !important;\n flex-grow: 0 !important;\n }\n .flex-xl-grow-1 {\n -ms-flex-positive: 1 !important;\n flex-grow: 1 !important;\n }\n .flex-xl-shrink-0 {\n -ms-flex-negative: 0 !important;\n flex-shrink: 0 !important;\n }\n .flex-xl-shrink-1 {\n -ms-flex-negative: 1 !important;\n flex-shrink: 1 !important;\n }\n .justify-content-xl-start {\n -ms-flex-pack: start !important;\n justify-content: flex-start !important;\n }\n .justify-content-xl-end {\n -ms-flex-pack: end !important;\n justify-content: flex-end !important;\n }\n .justify-content-xl-center {\n -ms-flex-pack: center !important;\n justify-content: center !important;\n }\n .justify-content-xl-between {\n -ms-flex-pack: justify !important;\n justify-content: space-between !important;\n }\n .justify-content-xl-around {\n -ms-flex-pack: distribute !important;\n justify-content: space-around !important;\n }\n .align-items-xl-start {\n -ms-flex-align: start !important;\n align-items: flex-start !important;\n }\n .align-items-xl-end {\n -ms-flex-align: end !important;\n align-items: flex-end !important;\n }\n .align-items-xl-center {\n -ms-flex-align: center !important;\n align-items: center !important;\n }\n .align-items-xl-baseline {\n -ms-flex-align: baseline !important;\n align-items: baseline !important;\n }\n .align-items-xl-stretch {\n -ms-flex-align: stretch !important;\n align-items: stretch !important;\n }\n .align-content-xl-start {\n -ms-flex-line-pack: start !important;\n align-content: flex-start !important;\n }\n .align-content-xl-end {\n -ms-flex-line-pack: end !important;\n align-content: flex-end !important;\n }\n .align-content-xl-center {\n -ms-flex-line-pack: center !important;\n align-content: center !important;\n }\n .align-content-xl-between {\n -ms-flex-line-pack: justify !important;\n align-content: space-between !important;\n }\n .align-content-xl-around {\n -ms-flex-line-pack: distribute !important;\n align-content: space-around !important;\n }\n .align-content-xl-stretch {\n -ms-flex-line-pack: stretch !important;\n align-content: stretch !important;\n }\n .align-self-xl-auto {\n -ms-flex-item-align: auto !important;\n align-self: auto !important;\n }\n .align-self-xl-start {\n -ms-flex-item-align: start !important;\n align-self: flex-start !important;\n }\n .align-self-xl-end {\n -ms-flex-item-align: end !important;\n align-self: flex-end !important;\n }\n .align-self-xl-center {\n -ms-flex-item-align: center !important;\n align-self: center !important;\n }\n .align-self-xl-baseline {\n -ms-flex-item-align: baseline !important;\n align-self: baseline !important;\n }\n .align-self-xl-stretch {\n -ms-flex-item-align: stretch !important;\n align-self: stretch !important;\n }\n}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.overflow-auto {\n overflow: auto !important;\n}\n\n.overflow-hidden {\n overflow: hidden !important;\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: -webkit-sticky !important;\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports ((position: -webkit-sticky) or (position: sticky)) {\n .sticky-top {\n position: -webkit-sticky;\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.min-vw-100 {\n min-width: 100vw !important;\n}\n\n.min-vh-100 {\n min-height: 100vh !important;\n}\n\n.vw-100 {\n width: 100vw !important;\n}\n\n.vh-100 {\n height: 100vh !important;\n}\n\n.stretched-link::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1;\n pointer-events: auto;\n content: \"\";\n background-color: rgba(0, 0, 0, 0);\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-n1 {\n margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n margin-left: -1rem !important;\n}\n\n.m-n4 {\n margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n margin-left: -3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-n1 {\n margin: -0.25rem !important;\n }\n .mt-sm-n1,\n .my-sm-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-sm-n1,\n .mx-sm-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-sm-n1,\n .my-sm-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-sm-n1,\n .mx-sm-n1 {\n margin-left: -0.25rem !important;\n }\n .m-sm-n2 {\n margin: -0.5rem !important;\n }\n .mt-sm-n2,\n .my-sm-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-sm-n2,\n .mx-sm-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-sm-n2,\n .my-sm-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-sm-n2,\n .mx-sm-n2 {\n margin-left: -0.5rem !important;\n }\n .m-sm-n3 {\n margin: -1rem !important;\n }\n .mt-sm-n3,\n .my-sm-n3 {\n margin-top: -1rem !important;\n }\n .mr-sm-n3,\n .mx-sm-n3 {\n margin-right: -1rem !important;\n }\n .mb-sm-n3,\n .my-sm-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-sm-n3,\n .mx-sm-n3 {\n margin-left: -1rem !important;\n }\n .m-sm-n4 {\n margin: -1.5rem !important;\n }\n .mt-sm-n4,\n .my-sm-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-sm-n4,\n .mx-sm-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-sm-n4,\n .my-sm-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-sm-n4,\n .mx-sm-n4 {\n margin-left: -1.5rem !important;\n }\n .m-sm-n5 {\n margin: -3rem !important;\n }\n .mt-sm-n5,\n .my-sm-n5 {\n margin-top: -3rem !important;\n }\n .mr-sm-n5,\n .mx-sm-n5 {\n margin-right: -3rem !important;\n }\n .mb-sm-n5,\n .my-sm-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-sm-n5,\n .mx-sm-n5 {\n margin-left: -3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-n1 {\n margin: -0.25rem !important;\n }\n .mt-md-n1,\n .my-md-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-md-n1,\n .mx-md-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-md-n1,\n .my-md-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-md-n1,\n .mx-md-n1 {\n margin-left: -0.25rem !important;\n }\n .m-md-n2 {\n margin: -0.5rem !important;\n }\n .mt-md-n2,\n .my-md-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-md-n2,\n .mx-md-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-md-n2,\n .my-md-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-md-n2,\n .mx-md-n2 {\n margin-left: -0.5rem !important;\n }\n .m-md-n3 {\n margin: -1rem !important;\n }\n .mt-md-n3,\n .my-md-n3 {\n margin-top: -1rem !important;\n }\n .mr-md-n3,\n .mx-md-n3 {\n margin-right: -1rem !important;\n }\n .mb-md-n3,\n .my-md-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-md-n3,\n .mx-md-n3 {\n margin-left: -1rem !important;\n }\n .m-md-n4 {\n margin: -1.5rem !important;\n }\n .mt-md-n4,\n .my-md-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-md-n4,\n .mx-md-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-md-n4,\n .my-md-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-md-n4,\n .mx-md-n4 {\n margin-left: -1.5rem !important;\n }\n .m-md-n5 {\n margin: -3rem !important;\n }\n .mt-md-n5,\n .my-md-n5 {\n margin-top: -3rem !important;\n }\n .mr-md-n5,\n .mx-md-n5 {\n margin-right: -3rem !important;\n }\n .mb-md-n5,\n .my-md-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-md-n5,\n .mx-md-n5 {\n margin-left: -3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-n1 {\n margin: -0.25rem !important;\n }\n .mt-lg-n1,\n .my-lg-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-lg-n1,\n .mx-lg-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-lg-n1,\n .my-lg-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-lg-n1,\n .mx-lg-n1 {\n margin-left: -0.25rem !important;\n }\n .m-lg-n2 {\n margin: -0.5rem !important;\n }\n .mt-lg-n2,\n .my-lg-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-lg-n2,\n .mx-lg-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-lg-n2,\n .my-lg-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-lg-n2,\n .mx-lg-n2 {\n margin-left: -0.5rem !important;\n }\n .m-lg-n3 {\n margin: -1rem !important;\n }\n .mt-lg-n3,\n .my-lg-n3 {\n margin-top: -1rem !important;\n }\n .mr-lg-n3,\n .mx-lg-n3 {\n margin-right: -1rem !important;\n }\n .mb-lg-n3,\n .my-lg-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-lg-n3,\n .mx-lg-n3 {\n margin-left: -1rem !important;\n }\n .m-lg-n4 {\n margin: -1.5rem !important;\n }\n .mt-lg-n4,\n .my-lg-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-lg-n4,\n .mx-lg-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-lg-n4,\n .my-lg-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-lg-n4,\n .mx-lg-n4 {\n margin-left: -1.5rem !important;\n }\n .m-lg-n5 {\n margin: -3rem !important;\n }\n .mt-lg-n5,\n .my-lg-n5 {\n margin-top: -3rem !important;\n }\n .mr-lg-n5,\n .mx-lg-n5 {\n margin-right: -3rem !important;\n }\n .mb-lg-n5,\n .my-lg-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-lg-n5,\n .mx-lg-n5 {\n margin-left: -3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-n1 {\n margin: -0.25rem !important;\n }\n .mt-xl-n1,\n .my-xl-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-xl-n1,\n .mx-xl-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-xl-n1,\n .my-xl-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-xl-n1,\n .mx-xl-n1 {\n margin-left: -0.25rem !important;\n }\n .m-xl-n2 {\n margin: -0.5rem !important;\n }\n .mt-xl-n2,\n .my-xl-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-xl-n2,\n .mx-xl-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-xl-n2,\n .my-xl-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-xl-n2,\n .mx-xl-n2 {\n margin-left: -0.5rem !important;\n }\n .m-xl-n3 {\n margin: -1rem !important;\n }\n .mt-xl-n3,\n .my-xl-n3 {\n margin-top: -1rem !important;\n }\n .mr-xl-n3,\n .mx-xl-n3 {\n margin-right: -1rem !important;\n }\n .mb-xl-n3,\n .my-xl-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-xl-n3,\n .mx-xl-n3 {\n margin-left: -1rem !important;\n }\n .m-xl-n4 {\n margin: -1.5rem !important;\n }\n .mt-xl-n4,\n .my-xl-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-xl-n4,\n .mx-xl-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-xl-n4,\n .my-xl-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-xl-n4,\n .mx-xl-n4 {\n margin-left: -1.5rem !important;\n }\n .m-xl-n5 {\n margin: -3rem !important;\n }\n .mt-xl-n5,\n .my-xl-n5 {\n margin-top: -3rem !important;\n }\n .mr-xl-n5,\n .mx-xl-n5 {\n margin-right: -3rem !important;\n }\n .mb-xl-n5,\n .my-xl-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-xl-n5,\n .mx-xl-n5 {\n margin-left: -3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !important;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-wrap {\n white-space: normal !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-lighter {\n font-weight: lighter !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-weight-bolder {\n font-weight: bolder !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0056b3 !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #494f54 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #19692c !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #0f6674 !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #ba8b00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #a71d2a !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #cbd3da !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #121416 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.text-decoration-none {\n text-decoration: none !important;\n}\n\n.text-break {\n word-break: break-word !important;\n overflow-wrap: break-word !important;\n}\n\n.text-reset {\n color: inherit !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n .table-dark {\n color: inherit;\n }\n .table-dark th,\n .table-dark td,\n .table-dark thead th,\n .table-dark tbody + tbody {\n border-color: #dee2e6;\n }\n .table .thead-dark th {\n color: inherit;\n border-color: #dee2e6;\n }\n}\n/*# sourceMappingURL=bootstrap.css.map */","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated font-resizing\n//\n// See https://github.com/twbs/rfs\n\n// Configuration\n\n// Base font size\n$rfs-base-font-size: 1.25rem !default;\n$rfs-font-size-unit: rem !default;\n\n// Breakpoint at where font-size starts decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n// Resize font-size 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// 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-responsive-font-sizes to false\n$enable-responsive-font-sizes: true !default;\n\n// Cache $rfs-base-font-size unit\n$rfs-base-font-size-unit: unit($rfs-base-font-size);\n\n// Remove px-unit from $rfs-base-font-size for calculations\n@if $rfs-base-font-size-unit == \"px\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 0 + 1);\n}\n@else if $rfs-base-font-size-unit == \"rem\" {\n $rfs-base-font-size: $rfs-base-font-size / ($rfs-base-font-size * 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: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == \"rem\" or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: $rfs-breakpoint / ($rfs-breakpoint * 0 + 1 / $rfs-rem-value);\n}\n\n// Responsive font-size mixin\n@mixin rfs($fs, $important: false) {\n // Cache $fs unit\n $fs-unit: if(type-of($fs) == \"number\", unit($fs), false);\n\n // Add !important suffix if needed\n $rfs-suffix: if($important, \" !important\", \"\");\n\n // If $fs isn't a number (like inherit) or $fs has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $fs-unit or $fs-unit != \"\" and $fs-unit != \"px\" and $fs-unit != \"rem\" or $fs == 0 {\n font-size: #{$fs}#{$rfs-suffix};\n }\n @else {\n // Variables for storing static and fluid rescaling\n $rfs-static: null;\n $rfs-fluid: null;\n\n // Remove px-unit from $fs for calculations\n @if $fs-unit == \"px\" {\n $fs: $fs / ($fs * 0 + 1);\n }\n @else if $fs-unit == \"rem\" {\n $fs: $fs / ($fs * 0 + 1 / $rfs-rem-value);\n }\n\n // Set default font-size\n @if $rfs-font-size-unit == rem {\n $rfs-static: #{$fs / $rfs-rem-value}rem#{$rfs-suffix};\n }\n @else if $rfs-font-size-unit == px {\n $rfs-static: #{$fs}px#{$rfs-suffix};\n }\n @else {\n @error \"`#{$rfs-font-size-unit}` is not a valid unit for $rfs-font-size-unit. Use `px` or `rem`.\";\n }\n\n // Only add media query if font-size is bigger as the minimum font-size\n // If $rfs-factor == 1, no rescaling will take place\n @if $fs > $rfs-base-font-size and $enable-responsive-font-sizes {\n $min-width: null;\n $variable-unit: null;\n\n // Calculate minimum font-size for given font-size\n $fs-min: $rfs-base-font-size + ($fs - $rfs-base-font-size) / $rfs-factor;\n\n // Calculate difference between given font-size and minimum font-size for given font-size\n $fs-diff: $fs - $fs-min;\n\n // Base font-size formatting\n // No need to check if the unit is valid, because we did that before\n $min-width: if($rfs-font-size-unit == rem, #{$fs-min / $rfs-rem-value}rem, #{$fs-min}px);\n\n // If two-dimensional, use smallest of screen width and height\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{$fs-diff * 100 / $rfs-breakpoint}#{$variable-unit};\n\n // Set the calculated font-size.\n $rfs-fluid: calc(#{$min-width} + #{$variable-width}) #{$rfs-suffix};\n }\n\n // Rendering\n @if $rfs-fluid == null {\n // Only render static font-size if no fluid font-size is available\n font-size: $rfs-static;\n }\n @else {\n $mq-value: null;\n\n // RFS breakpoint formatting\n @if $rfs-breakpoint-unit == em or $rfs-breakpoint-unit == rem {\n $mq-value: #{$rfs-breakpoint / $rfs-rem-value}#{$rfs-breakpoint-unit};\n }\n @else if $rfs-breakpoint-unit == px {\n $mq-value: #{$rfs-breakpoint}px;\n }\n @else {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n }\n\n @if $rfs-class == \"disable\" {\n // Adding an extra class increases specificity,\n // which prevents the media query to override the font size\n &,\n .disable-responsive-font-size &,\n &.disable-responsive-font-size {\n font-size: $rfs-static;\n }\n }\n @else {\n font-size: $rfs-static;\n }\n\n @if $rfs-two-dimensional {\n @media (max-width: #{$mq-value}), (max-height: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n @else {\n @media (max-width: #{$mq-value}) {\n @if $rfs-class == \"enable\" {\n .enable-responsive-font-size &,\n &.enable-responsive-font-size {\n font-size: $rfs-fluid;\n }\n }\n @else {\n font-size: $rfs-fluid;\n }\n\n @if $rfs-safari-iframe-resize-bug-fix {\n // stylelint-disable-next-line length-zero-no-unit\n min-width: 0vw;\n }\n }\n }\n }\n }\n}\n\n// The font-size & responsive-font-size mixin uses RFS to rescale font sizes\n@mixin font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n\n@mixin responsive-font-size($fs, $important: false) {\n @include rfs($fs, $important);\n}\n","/*!\n * Bootstrap v4.3.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n */\n:root {\n --blue: #007bff;\n --indigo: #6610f2;\n --purple: #6f42c1;\n --pink: #e83e8c;\n --red: #dc3545;\n --orange: #fd7e14;\n --yellow: #ffc107;\n --green: #28a745;\n --teal: #20c997;\n --cyan: #17a2b8;\n --white: #fff;\n --gray: #6c757d;\n --gray-dark: #343a40;\n --primary: #007bff;\n --secondary: #6c757d;\n --success: #28a745;\n --info: #17a2b8;\n --warning: #ffc107;\n --danger: #dc3545;\n --light: #f8f9fa;\n --dark: #343a40;\n --breakpoint-xs: 0;\n --breakpoint-sm: 576px;\n --breakpoint-md: 768px;\n --breakpoint-lg: 992px;\n --breakpoint-xl: 1200px;\n --font-family-sans-serif: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n}\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\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,\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: .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: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\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,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nselect {\n word-wrap: normal;\n}\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton:not(:disabled),\n[type=\"button\"]:not(:disabled),\n[type=\"reset\"]:not(:disabled),\n[type=\"submit\"]:not(:disabled) {\n cursor: pointer;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: 0.5rem;\n font-weight: 500;\n line-height: 1.2;\n}\n\nh1, .h1 {\n font-size: 2.5rem;\n}\n\nh2, .h2 {\n font-size: 2rem;\n}\n\nh3, .h3 {\n font-size: 1.75rem;\n}\n\nh4, .h4 {\n font-size: 1.5rem;\n}\n\nh5, .h5 {\n font-size: 1.25rem;\n}\n\nh6, .h6 {\n font-size: 1rem;\n}\n\n.lead {\n font-size: 1.25rem;\n font-weight: 300;\n}\n\n.display-1 {\n font-size: 6rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-2 {\n font-size: 5.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-3 {\n font-size: 4.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\n.display-4 {\n font-size: 3.5rem;\n font-weight: 300;\n line-height: 1.2;\n}\n\nhr {\n margin-top: 1rem;\n margin-bottom: 1rem;\n border: 0;\n border-top: 1px solid rgba(0, 0, 0, 0.1);\n}\n\nsmall,\n.small {\n font-size: 80%;\n font-weight: 400;\n}\n\nmark,\n.mark {\n padding: 0.2em;\n background-color: #fcf8e3;\n}\n\n.list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline {\n padding-left: 0;\n list-style: none;\n}\n\n.list-inline-item {\n display: inline-block;\n}\n\n.list-inline-item:not(:last-child) {\n margin-right: 0.5rem;\n}\n\n.initialism {\n font-size: 90%;\n text-transform: uppercase;\n}\n\n.blockquote {\n margin-bottom: 1rem;\n font-size: 1.25rem;\n}\n\n.blockquote-footer {\n display: block;\n font-size: 80%;\n color: #6c757d;\n}\n\n.blockquote-footer::before {\n content: \"\\2014\\00A0\";\n}\n\n.img-fluid {\n max-width: 100%;\n height: auto;\n}\n\n.img-thumbnail {\n padding: 0.25rem;\n background-color: #fff;\n border: 1px solid #dee2e6;\n border-radius: 0.25rem;\n max-width: 100%;\n height: auto;\n}\n\n.figure {\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: 0.5rem;\n line-height: 1;\n}\n\n.figure-caption {\n font-size: 90%;\n color: #6c757d;\n}\n\ncode {\n font-size: 87.5%;\n color: #e83e8c;\n word-break: break-word;\n}\n\na > code {\n color: inherit;\n}\n\nkbd {\n padding: 0.2rem 0.4rem;\n font-size: 87.5%;\n color: #fff;\n background-color: #212529;\n border-radius: 0.2rem;\n}\n\nkbd kbd {\n padding: 0;\n font-size: 100%;\n font-weight: 700;\n}\n\npre {\n display: block;\n font-size: 87.5%;\n color: #212529;\n}\n\npre code {\n font-size: inherit;\n color: inherit;\n word-break: normal;\n}\n\n.pre-scrollable {\n max-height: 340px;\n overflow-y: scroll;\n}\n\n.container {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n@media (min-width: 576px) {\n .container {\n max-width: 540px;\n }\n}\n\n@media (min-width: 768px) {\n .container {\n max-width: 720px;\n }\n}\n\n@media (min-width: 992px) {\n .container {\n max-width: 960px;\n }\n}\n\n@media (min-width: 1200px) {\n .container {\n max-width: 1140px;\n }\n}\n\n.container-fluid {\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n margin-right: auto;\n margin-left: auto;\n}\n\n.row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -15px;\n margin-left: -15px;\n}\n\n.no-gutters {\n margin-right: 0;\n margin-left: 0;\n}\n\n.no-gutters > .col,\n.no-gutters > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\n}\n\n.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,\n.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,\n.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,\n.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,\n.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,\n.col-xl-auto {\n position: relative;\n width: 100%;\n padding-right: 15px;\n padding-left: 15px;\n}\n\n.col {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n}\n\n.col-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n}\n\n.col-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n}\n\n.col-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n}\n\n.col-3 {\n flex: 0 0 25%;\n max-width: 25%;\n}\n\n.col-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n}\n\n.col-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n}\n\n.col-6 {\n flex: 0 0 50%;\n max-width: 50%;\n}\n\n.col-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n}\n\n.col-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n}\n\n.col-9 {\n flex: 0 0 75%;\n max-width: 75%;\n}\n\n.col-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n}\n\n.col-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n}\n\n.col-12 {\n flex: 0 0 100%;\n max-width: 100%;\n}\n\n.order-first {\n order: -1;\n}\n\n.order-last {\n order: 13;\n}\n\n.order-0 {\n order: 0;\n}\n\n.order-1 {\n order: 1;\n}\n\n.order-2 {\n order: 2;\n}\n\n.order-3 {\n order: 3;\n}\n\n.order-4 {\n order: 4;\n}\n\n.order-5 {\n order: 5;\n}\n\n.order-6 {\n order: 6;\n}\n\n.order-7 {\n order: 7;\n}\n\n.order-8 {\n order: 8;\n}\n\n.order-9 {\n order: 9;\n}\n\n.order-10 {\n order: 10;\n}\n\n.order-11 {\n order: 11;\n}\n\n.order-12 {\n order: 12;\n}\n\n.offset-1 {\n margin-left: 8.333333%;\n}\n\n.offset-2 {\n margin-left: 16.666667%;\n}\n\n.offset-3 {\n margin-left: 25%;\n}\n\n.offset-4 {\n margin-left: 33.333333%;\n}\n\n.offset-5 {\n margin-left: 41.666667%;\n}\n\n.offset-6 {\n margin-left: 50%;\n}\n\n.offset-7 {\n margin-left: 58.333333%;\n}\n\n.offset-8 {\n margin-left: 66.666667%;\n}\n\n.offset-9 {\n margin-left: 75%;\n}\n\n.offset-10 {\n margin-left: 83.333333%;\n}\n\n.offset-11 {\n margin-left: 91.666667%;\n}\n\n@media (min-width: 576px) {\n .col-sm {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-sm-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-sm-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-sm-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-sm-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-sm-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-sm-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-sm-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-sm-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-sm-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-sm-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-sm-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-sm-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-sm-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-sm-first {\n order: -1;\n }\n .order-sm-last {\n order: 13;\n }\n .order-sm-0 {\n order: 0;\n }\n .order-sm-1 {\n order: 1;\n }\n .order-sm-2 {\n order: 2;\n }\n .order-sm-3 {\n order: 3;\n }\n .order-sm-4 {\n order: 4;\n }\n .order-sm-5 {\n order: 5;\n }\n .order-sm-6 {\n order: 6;\n }\n .order-sm-7 {\n order: 7;\n }\n .order-sm-8 {\n order: 8;\n }\n .order-sm-9 {\n order: 9;\n }\n .order-sm-10 {\n order: 10;\n }\n .order-sm-11 {\n order: 11;\n }\n .order-sm-12 {\n order: 12;\n }\n .offset-sm-0 {\n margin-left: 0;\n }\n .offset-sm-1 {\n margin-left: 8.333333%;\n }\n .offset-sm-2 {\n margin-left: 16.666667%;\n }\n .offset-sm-3 {\n margin-left: 25%;\n }\n .offset-sm-4 {\n margin-left: 33.333333%;\n }\n .offset-sm-5 {\n margin-left: 41.666667%;\n }\n .offset-sm-6 {\n margin-left: 50%;\n }\n .offset-sm-7 {\n margin-left: 58.333333%;\n }\n .offset-sm-8 {\n margin-left: 66.666667%;\n }\n .offset-sm-9 {\n margin-left: 75%;\n }\n .offset-sm-10 {\n margin-left: 83.333333%;\n }\n .offset-sm-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 768px) {\n .col-md {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-md-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-md-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-md-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-md-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-md-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-md-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-md-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-md-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-md-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-md-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-md-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-md-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-md-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-md-first {\n order: -1;\n }\n .order-md-last {\n order: 13;\n }\n .order-md-0 {\n order: 0;\n }\n .order-md-1 {\n order: 1;\n }\n .order-md-2 {\n order: 2;\n }\n .order-md-3 {\n order: 3;\n }\n .order-md-4 {\n order: 4;\n }\n .order-md-5 {\n order: 5;\n }\n .order-md-6 {\n order: 6;\n }\n .order-md-7 {\n order: 7;\n }\n .order-md-8 {\n order: 8;\n }\n .order-md-9 {\n order: 9;\n }\n .order-md-10 {\n order: 10;\n }\n .order-md-11 {\n order: 11;\n }\n .order-md-12 {\n order: 12;\n }\n .offset-md-0 {\n margin-left: 0;\n }\n .offset-md-1 {\n margin-left: 8.333333%;\n }\n .offset-md-2 {\n margin-left: 16.666667%;\n }\n .offset-md-3 {\n margin-left: 25%;\n }\n .offset-md-4 {\n margin-left: 33.333333%;\n }\n .offset-md-5 {\n margin-left: 41.666667%;\n }\n .offset-md-6 {\n margin-left: 50%;\n }\n .offset-md-7 {\n margin-left: 58.333333%;\n }\n .offset-md-8 {\n margin-left: 66.666667%;\n }\n .offset-md-9 {\n margin-left: 75%;\n }\n .offset-md-10 {\n margin-left: 83.333333%;\n }\n .offset-md-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 992px) {\n .col-lg {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-lg-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-lg-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-lg-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-lg-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-lg-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-lg-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-lg-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-lg-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-lg-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-lg-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-lg-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-lg-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-lg-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-lg-first {\n order: -1;\n }\n .order-lg-last {\n order: 13;\n }\n .order-lg-0 {\n order: 0;\n }\n .order-lg-1 {\n order: 1;\n }\n .order-lg-2 {\n order: 2;\n }\n .order-lg-3 {\n order: 3;\n }\n .order-lg-4 {\n order: 4;\n }\n .order-lg-5 {\n order: 5;\n }\n .order-lg-6 {\n order: 6;\n }\n .order-lg-7 {\n order: 7;\n }\n .order-lg-8 {\n order: 8;\n }\n .order-lg-9 {\n order: 9;\n }\n .order-lg-10 {\n order: 10;\n }\n .order-lg-11 {\n order: 11;\n }\n .order-lg-12 {\n order: 12;\n }\n .offset-lg-0 {\n margin-left: 0;\n }\n .offset-lg-1 {\n margin-left: 8.333333%;\n }\n .offset-lg-2 {\n margin-left: 16.666667%;\n }\n .offset-lg-3 {\n margin-left: 25%;\n }\n .offset-lg-4 {\n margin-left: 33.333333%;\n }\n .offset-lg-5 {\n margin-left: 41.666667%;\n }\n .offset-lg-6 {\n margin-left: 50%;\n }\n .offset-lg-7 {\n margin-left: 58.333333%;\n }\n .offset-lg-8 {\n margin-left: 66.666667%;\n }\n .offset-lg-9 {\n margin-left: 75%;\n }\n .offset-lg-10 {\n margin-left: 83.333333%;\n }\n .offset-lg-11 {\n margin-left: 91.666667%;\n }\n}\n\n@media (min-width: 1200px) {\n .col-xl {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col-xl-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%;\n }\n .col-xl-1 {\n flex: 0 0 8.333333%;\n max-width: 8.333333%;\n }\n .col-xl-2 {\n flex: 0 0 16.666667%;\n max-width: 16.666667%;\n }\n .col-xl-3 {\n flex: 0 0 25%;\n max-width: 25%;\n }\n .col-xl-4 {\n flex: 0 0 33.333333%;\n max-width: 33.333333%;\n }\n .col-xl-5 {\n flex: 0 0 41.666667%;\n max-width: 41.666667%;\n }\n .col-xl-6 {\n flex: 0 0 50%;\n max-width: 50%;\n }\n .col-xl-7 {\n flex: 0 0 58.333333%;\n max-width: 58.333333%;\n }\n .col-xl-8 {\n flex: 0 0 66.666667%;\n max-width: 66.666667%;\n }\n .col-xl-9 {\n flex: 0 0 75%;\n max-width: 75%;\n }\n .col-xl-10 {\n flex: 0 0 83.333333%;\n max-width: 83.333333%;\n }\n .col-xl-11 {\n flex: 0 0 91.666667%;\n max-width: 91.666667%;\n }\n .col-xl-12 {\n flex: 0 0 100%;\n max-width: 100%;\n }\n .order-xl-first {\n order: -1;\n }\n .order-xl-last {\n order: 13;\n }\n .order-xl-0 {\n order: 0;\n }\n .order-xl-1 {\n order: 1;\n }\n .order-xl-2 {\n order: 2;\n }\n .order-xl-3 {\n order: 3;\n }\n .order-xl-4 {\n order: 4;\n }\n .order-xl-5 {\n order: 5;\n }\n .order-xl-6 {\n order: 6;\n }\n .order-xl-7 {\n order: 7;\n }\n .order-xl-8 {\n order: 8;\n }\n .order-xl-9 {\n order: 9;\n }\n .order-xl-10 {\n order: 10;\n }\n .order-xl-11 {\n order: 11;\n }\n .order-xl-12 {\n order: 12;\n }\n .offset-xl-0 {\n margin-left: 0;\n }\n .offset-xl-1 {\n margin-left: 8.333333%;\n }\n .offset-xl-2 {\n margin-left: 16.666667%;\n }\n .offset-xl-3 {\n margin-left: 25%;\n }\n .offset-xl-4 {\n margin-left: 33.333333%;\n }\n .offset-xl-5 {\n margin-left: 41.666667%;\n }\n .offset-xl-6 {\n margin-left: 50%;\n }\n .offset-xl-7 {\n margin-left: 58.333333%;\n }\n .offset-xl-8 {\n margin-left: 66.666667%;\n }\n .offset-xl-9 {\n margin-left: 75%;\n }\n .offset-xl-10 {\n margin-left: 83.333333%;\n }\n .offset-xl-11 {\n margin-left: 91.666667%;\n }\n}\n\n.table {\n width: 100%;\n margin-bottom: 1rem;\n color: #212529;\n}\n\n.table th,\n.table td {\n padding: 0.75rem;\n vertical-align: top;\n border-top: 1px solid #dee2e6;\n}\n\n.table thead th {\n vertical-align: bottom;\n border-bottom: 2px solid #dee2e6;\n}\n\n.table tbody + tbody {\n border-top: 2px solid #dee2e6;\n}\n\n.table-sm th,\n.table-sm td {\n padding: 0.3rem;\n}\n\n.table-bordered {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered th,\n.table-bordered td {\n border: 1px solid #dee2e6;\n}\n\n.table-bordered thead th,\n.table-bordered thead td {\n border-bottom-width: 2px;\n}\n\n.table-borderless th,\n.table-borderless td,\n.table-borderless thead th,\n.table-borderless tbody + tbody {\n border: 0;\n}\n\n.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(0, 0, 0, 0.05);\n}\n\n.table-hover tbody tr:hover {\n color: #212529;\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-primary,\n.table-primary > th,\n.table-primary > td {\n background-color: #b8daff;\n}\n\n.table-primary th,\n.table-primary td,\n.table-primary thead th,\n.table-primary tbody + tbody {\n border-color: #7abaff;\n}\n\n.table-hover .table-primary:hover {\n background-color: #9fcdff;\n}\n\n.table-hover .table-primary:hover > td,\n.table-hover .table-primary:hover > th {\n background-color: #9fcdff;\n}\n\n.table-secondary,\n.table-secondary > th,\n.table-secondary > td {\n background-color: #d6d8db;\n}\n\n.table-secondary th,\n.table-secondary td,\n.table-secondary thead th,\n.table-secondary tbody + tbody {\n border-color: #b3b7bb;\n}\n\n.table-hover .table-secondary:hover {\n background-color: #c8cbcf;\n}\n\n.table-hover .table-secondary:hover > td,\n.table-hover .table-secondary:hover > th {\n background-color: #c8cbcf;\n}\n\n.table-success,\n.table-success > th,\n.table-success > td {\n background-color: #c3e6cb;\n}\n\n.table-success th,\n.table-success td,\n.table-success thead th,\n.table-success tbody + tbody {\n border-color: #8fd19e;\n}\n\n.table-hover .table-success:hover {\n background-color: #b1dfbb;\n}\n\n.table-hover .table-success:hover > td,\n.table-hover .table-success:hover > th {\n background-color: #b1dfbb;\n}\n\n.table-info,\n.table-info > th,\n.table-info > td {\n background-color: #bee5eb;\n}\n\n.table-info th,\n.table-info td,\n.table-info thead th,\n.table-info tbody + tbody {\n border-color: #86cfda;\n}\n\n.table-hover .table-info:hover {\n background-color: #abdde5;\n}\n\n.table-hover .table-info:hover > td,\n.table-hover .table-info:hover > th {\n background-color: #abdde5;\n}\n\n.table-warning,\n.table-warning > th,\n.table-warning > td {\n background-color: #ffeeba;\n}\n\n.table-warning th,\n.table-warning td,\n.table-warning thead th,\n.table-warning tbody + tbody {\n border-color: #ffdf7e;\n}\n\n.table-hover .table-warning:hover {\n background-color: #ffe8a1;\n}\n\n.table-hover .table-warning:hover > td,\n.table-hover .table-warning:hover > th {\n background-color: #ffe8a1;\n}\n\n.table-danger,\n.table-danger > th,\n.table-danger > td {\n background-color: #f5c6cb;\n}\n\n.table-danger th,\n.table-danger td,\n.table-danger thead th,\n.table-danger tbody + tbody {\n border-color: #ed969e;\n}\n\n.table-hover .table-danger:hover {\n background-color: #f1b0b7;\n}\n\n.table-hover .table-danger:hover > td,\n.table-hover .table-danger:hover > th {\n background-color: #f1b0b7;\n}\n\n.table-light,\n.table-light > th,\n.table-light > td {\n background-color: #fdfdfe;\n}\n\n.table-light th,\n.table-light td,\n.table-light thead th,\n.table-light tbody + tbody {\n border-color: #fbfcfc;\n}\n\n.table-hover .table-light:hover {\n background-color: #ececf6;\n}\n\n.table-hover .table-light:hover > td,\n.table-hover .table-light:hover > th {\n background-color: #ececf6;\n}\n\n.table-dark,\n.table-dark > th,\n.table-dark > td {\n background-color: #c6c8ca;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th,\n.table-dark tbody + tbody {\n border-color: #95999c;\n}\n\n.table-hover .table-dark:hover {\n background-color: #b9bbbe;\n}\n\n.table-hover .table-dark:hover > td,\n.table-hover .table-dark:hover > th {\n background-color: #b9bbbe;\n}\n\n.table-active,\n.table-active > th,\n.table-active > td {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table-hover .table-active:hover > td,\n.table-hover .table-active:hover > th {\n background-color: rgba(0, 0, 0, 0.075);\n}\n\n.table .thead-dark th {\n color: #fff;\n background-color: #343a40;\n border-color: #454d55;\n}\n\n.table .thead-light th {\n color: #495057;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.table-dark {\n color: #fff;\n background-color: #343a40;\n}\n\n.table-dark th,\n.table-dark td,\n.table-dark thead th {\n border-color: #454d55;\n}\n\n.table-dark.table-bordered {\n border: 0;\n}\n\n.table-dark.table-striped tbody tr:nth-of-type(odd) {\n background-color: rgba(255, 255, 255, 0.05);\n}\n\n.table-dark.table-hover tbody tr:hover {\n color: #fff;\n background-color: rgba(255, 255, 255, 0.075);\n}\n\n@media (max-width: 575.98px) {\n .table-responsive-sm {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-sm > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 767.98px) {\n .table-responsive-md {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-md > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 991.98px) {\n .table-responsive-lg {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-lg > .table-bordered {\n border: 0;\n }\n}\n\n@media (max-width: 1199.98px) {\n .table-responsive-xl {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n }\n .table-responsive-xl > .table-bordered {\n border: 0;\n }\n}\n\n.table-responsive {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.table-responsive > .table-bordered {\n border: 0;\n}\n\n.form-control {\n display: block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .form-control {\n transition: none;\n }\n}\n\n.form-control::-ms-expand {\n background-color: transparent;\n border: 0;\n}\n\n.form-control:focus {\n color: #495057;\n background-color: #fff;\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.form-control::placeholder {\n color: #6c757d;\n opacity: 1;\n}\n\n.form-control:disabled, .form-control[readonly] {\n background-color: #e9ecef;\n opacity: 1;\n}\n\nselect.form-control:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n.col-form-label {\n padding-top: calc(0.375rem + 1px);\n padding-bottom: calc(0.375rem + 1px);\n margin-bottom: 0;\n font-size: inherit;\n line-height: 1.5;\n}\n\n.col-form-label-lg {\n padding-top: calc(0.5rem + 1px);\n padding-bottom: calc(0.5rem + 1px);\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.col-form-label-sm {\n padding-top: calc(0.25rem + 1px);\n padding-bottom: calc(0.25rem + 1px);\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.form-control-plaintext {\n display: block;\n width: 100%;\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n margin-bottom: 0;\n line-height: 1.5;\n color: #212529;\n background-color: transparent;\n border: solid transparent;\n border-width: 1px 0;\n}\n\n.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {\n padding-right: 0;\n padding-left: 0;\n}\n\n.form-control-sm {\n height: calc(1.5em + 0.5rem + 2px);\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.form-control-lg {\n height: calc(1.5em + 1rem + 2px);\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\nselect.form-control[size], select.form-control[multiple] {\n height: auto;\n}\n\ntextarea.form-control {\n height: auto;\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-text {\n display: block;\n margin-top: 0.25rem;\n}\n\n.form-row {\n display: flex;\n flex-wrap: wrap;\n margin-right: -5px;\n margin-left: -5px;\n}\n\n.form-row > .col,\n.form-row > [class*=\"col-\"] {\n padding-right: 5px;\n padding-left: 5px;\n}\n\n.form-check {\n position: relative;\n display: block;\n padding-left: 1.25rem;\n}\n\n.form-check-input {\n position: absolute;\n margin-top: 0.3rem;\n margin-left: -1.25rem;\n}\n\n.form-check-input:disabled ~ .form-check-label {\n color: #6c757d;\n}\n\n.form-check-label {\n margin-bottom: 0;\n}\n\n.form-check-inline {\n display: inline-flex;\n align-items: center;\n padding-left: 0;\n margin-right: 0.75rem;\n}\n\n.form-check-inline .form-check-input {\n position: static;\n margin-top: 0;\n margin-right: 0.3125rem;\n margin-left: 0;\n}\n\n.valid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #28a745;\n}\n\n.valid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: .1rem;\n font-size: 0.875rem;\n line-height: 1.5;\n color: #fff;\n background-color: rgba(40, 167, 69, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated .form-control:valid, .form-control.is-valid {\n border-color: #28a745;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\");\n background-repeat: no-repeat;\n background-position: center right calc(0.375em + 0.1875rem);\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:valid:focus, .form-control.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .form-control:valid ~ .valid-feedback,\n.was-validated .form-control:valid ~ .valid-tooltip, .form-control.is-valid ~ .valid-feedback,\n.form-control.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated textarea.form-control:valid, textarea.form-control.is-valid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:valid, .custom-select.is-valid {\n border-color: #28a745;\n padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem);\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px, url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e\") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-select:valid ~ .valid-feedback,\n.was-validated .custom-select:valid ~ .valid-tooltip, .custom-select.is-valid ~ .valid-feedback,\n.custom-select.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:valid ~ .valid-feedback,\n.was-validated .form-control-file:valid ~ .valid-tooltip, .form-control-file.is-valid ~ .valid-feedback,\n.form-control-file.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {\n color: #28a745;\n}\n\n.was-validated .form-check-input:valid ~ .valid-feedback,\n.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,\n.form-check-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {\n color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {\n border-color: #28a745;\n}\n\n.was-validated .custom-control-input:valid ~ .valid-feedback,\n.was-validated .custom-control-input:valid ~ .valid-tooltip, .custom-control-input.is-valid ~ .valid-feedback,\n.custom-control-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {\n border-color: #34ce57;\n background-color: #34ce57;\n}\n\n.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {\n border-color: #28a745;\n}\n\n.was-validated .custom-file-input:valid ~ .valid-feedback,\n.was-validated .custom-file-input:valid ~ .valid-tooltip, .custom-file-input.is-valid ~ .valid-feedback,\n.custom-file-input.is-valid ~ .valid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {\n border-color: #28a745;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);\n}\n\n.invalid-feedback {\n display: none;\n width: 100%;\n margin-top: 0.25rem;\n font-size: 80%;\n color: #dc3545;\n}\n\n.invalid-tooltip {\n position: absolute;\n top: 100%;\n z-index: 5;\n display: none;\n max-width: 100%;\n padding: 0.25rem 0.5rem;\n margin-top: .1rem;\n font-size: 0.875rem;\n line-height: 1.5;\n color: #fff;\n background-color: rgba(220, 53, 69, 0.9);\n border-radius: 0.25rem;\n}\n\n.was-validated .form-control:invalid, .form-control.is-invalid {\n border-color: #dc3545;\n padding-right: calc(1.5em + 0.75rem);\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E\");\n background-repeat: no-repeat;\n background-position: center right calc(0.375em + 0.1875rem);\n background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .form-control:invalid ~ .invalid-feedback,\n.was-validated .form-control:invalid ~ .invalid-tooltip, .form-control.is-invalid ~ .invalid-feedback,\n.form-control.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {\n padding-right: calc(1.5em + 0.75rem);\n background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);\n}\n\n.was-validated .custom-select:invalid, .custom-select.is-invalid {\n border-color: #dc3545;\n padding-right: calc((1em + 0.75rem) * 3 / 4 + 1.75rem);\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px, url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23dc3545' viewBox='-2 -2 7 7'%3e%3cpath stroke='%23dc3545' d='M0 0l3 3m0-3L0 3'/%3e%3ccircle r='.5'/%3e%3ccircle cx='3' r='.5'/%3e%3ccircle cy='3' r='.5'/%3e%3ccircle cx='3' cy='3' r='.5'/%3e%3c/svg%3E\") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);\n}\n\n.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-select:invalid ~ .invalid-feedback,\n.was-validated .custom-select:invalid ~ .invalid-tooltip, .custom-select.is-invalid ~ .invalid-feedback,\n.custom-select.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-control-file:invalid ~ .invalid-feedback,\n.was-validated .form-control-file:invalid ~ .invalid-tooltip, .form-control-file.is-invalid ~ .invalid-feedback,\n.form-control-file.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {\n color: #dc3545;\n}\n\n.was-validated .form-check-input:invalid ~ .invalid-feedback,\n.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,\n.form-check-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {\n color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {\n border-color: #dc3545;\n}\n\n.was-validated .custom-control-input:invalid ~ .invalid-feedback,\n.was-validated .custom-control-input:invalid ~ .invalid-tooltip, .custom-control-input.is-invalid ~ .invalid-feedback,\n.custom-control-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {\n border-color: #e4606d;\n background-color: #e4606d;\n}\n\n.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {\n border-color: #dc3545;\n}\n\n.was-validated .custom-file-input:invalid ~ .invalid-feedback,\n.was-validated .custom-file-input:invalid ~ .invalid-tooltip, .custom-file-input.is-invalid ~ .invalid-feedback,\n.custom-file-input.is-invalid ~ .invalid-tooltip {\n display: block;\n}\n\n.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {\n border-color: #dc3545;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);\n}\n\n.form-inline {\n display: flex;\n flex-flow: row wrap;\n align-items: center;\n}\n\n.form-inline .form-check {\n width: 100%;\n}\n\n@media (min-width: 576px) {\n .form-inline label {\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 0;\n }\n .form-inline .form-group {\n display: flex;\n flex: 0 0 auto;\n flex-flow: row wrap;\n align-items: center;\n margin-bottom: 0;\n }\n .form-inline .form-control {\n display: inline-block;\n width: auto;\n vertical-align: middle;\n }\n .form-inline .form-control-plaintext {\n display: inline-block;\n }\n .form-inline .input-group,\n .form-inline .custom-select {\n width: auto;\n }\n .form-inline .form-check {\n display: flex;\n align-items: center;\n justify-content: center;\n width: auto;\n padding-left: 0;\n }\n .form-inline .form-check-input {\n position: relative;\n flex-shrink: 0;\n margin-top: 0;\n margin-right: 0.25rem;\n margin-left: 0;\n }\n .form-inline .custom-control {\n align-items: center;\n justify-content: center;\n }\n .form-inline .custom-control-label {\n margin-bottom: 0;\n }\n}\n\n.btn {\n display: inline-block;\n font-weight: 400;\n color: #212529;\n text-align: center;\n vertical-align: middle;\n user-select: none;\n background-color: transparent;\n border: 1px solid transparent;\n padding: 0.375rem 0.75rem;\n font-size: 1rem;\n line-height: 1.5;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .btn {\n transition: none;\n }\n}\n\n.btn:hover {\n color: #212529;\n text-decoration: none;\n}\n\n.btn:focus, .btn.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.btn.disabled, .btn:disabled {\n opacity: 0.65;\n}\n\na.btn.disabled,\nfieldset:disabled a.btn {\n pointer-events: none;\n}\n\n.btn-primary {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:hover {\n color: #fff;\n background-color: #0069d9;\n border-color: #0062cc;\n}\n\n.btn-primary:focus, .btn-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);\n}\n\n.btn-primary.disabled, .btn-primary:disabled {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,\n.show > .btn-primary.dropdown-toggle {\n color: #fff;\n background-color: #0062cc;\n border-color: #005cbf;\n}\n\n.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);\n}\n\n.btn-secondary {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:hover {\n color: #fff;\n background-color: #5a6268;\n border-color: #545b62;\n}\n\n.btn-secondary:focus, .btn-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);\n}\n\n.btn-secondary.disabled, .btn-secondary:disabled {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-secondary.dropdown-toggle {\n color: #fff;\n background-color: #545b62;\n border-color: #4e555b;\n}\n\n.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);\n}\n\n.btn-success {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:hover {\n color: #fff;\n background-color: #218838;\n border-color: #1e7e34;\n}\n\n.btn-success:focus, .btn-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);\n}\n\n.btn-success.disabled, .btn-success:disabled {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,\n.show > .btn-success.dropdown-toggle {\n color: #fff;\n background-color: #1e7e34;\n border-color: #1c7430;\n}\n\n.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);\n}\n\n.btn-info {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:hover {\n color: #fff;\n background-color: #138496;\n border-color: #117a8b;\n}\n\n.btn-info:focus, .btn-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n}\n\n.btn-info.disabled, .btn-info:disabled {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,\n.show > .btn-info.dropdown-toggle {\n color: #fff;\n background-color: #117a8b;\n border-color: #10707f;\n}\n\n.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);\n}\n\n.btn-warning {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:hover {\n color: #212529;\n background-color: #e0a800;\n border-color: #d39e00;\n}\n\n.btn-warning:focus, .btn-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);\n}\n\n.btn-warning.disabled, .btn-warning:disabled {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,\n.show > .btn-warning.dropdown-toggle {\n color: #212529;\n background-color: #d39e00;\n border-color: #c69500;\n}\n\n.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);\n}\n\n.btn-danger {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:hover {\n color: #fff;\n background-color: #c82333;\n border-color: #bd2130;\n}\n\n.btn-danger:focus, .btn-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n}\n\n.btn-danger.disabled, .btn-danger:disabled {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,\n.show > .btn-danger.dropdown-toggle {\n color: #fff;\n background-color: #bd2130;\n border-color: #b21f2d;\n}\n\n.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);\n}\n\n.btn-light {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:hover {\n color: #212529;\n background-color: #e2e6ea;\n border-color: #dae0e5;\n}\n\n.btn-light:focus, .btn-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);\n}\n\n.btn-light.disabled, .btn-light:disabled {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,\n.show > .btn-light.dropdown-toggle {\n color: #212529;\n background-color: #dae0e5;\n border-color: #d3d9df;\n}\n\n.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);\n}\n\n.btn-dark {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:hover {\n color: #fff;\n background-color: #23272b;\n border-color: #1d2124;\n}\n\n.btn-dark:focus, .btn-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);\n}\n\n.btn-dark.disabled, .btn-dark:disabled {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,\n.show > .btn-dark.dropdown-toggle {\n color: #fff;\n background-color: #1d2124;\n border-color: #171a1d;\n}\n\n.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);\n}\n\n.btn-outline-primary {\n color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:hover {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:focus, .btn-outline-primary.focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-primary.disabled, .btn-outline-primary:disabled {\n color: #007bff;\n background-color: transparent;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-primary.dropdown-toggle {\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-primary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.btn-outline-secondary {\n color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:hover {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:focus, .btn-outline-secondary.focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {\n color: #6c757d;\n background-color: transparent;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,\n.show > .btn-outline-secondary.dropdown-toggle {\n color: #fff;\n background-color: #6c757d;\n border-color: #6c757d;\n}\n\n.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-secondary.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.btn-outline-success {\n color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:hover {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:focus, .btn-outline-success.focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-success.disabled, .btn-outline-success:disabled {\n color: #28a745;\n background-color: transparent;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,\n.show > .btn-outline-success.dropdown-toggle {\n color: #fff;\n background-color: #28a745;\n border-color: #28a745;\n}\n\n.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-success.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.btn-outline-info {\n color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:hover {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:focus, .btn-outline-info.focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-info.disabled, .btn-outline-info:disabled {\n color: #17a2b8;\n background-color: transparent;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,\n.show > .btn-outline-info.dropdown-toggle {\n color: #fff;\n background-color: #17a2b8;\n border-color: #17a2b8;\n}\n\n.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-info.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.btn-outline-warning {\n color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:hover {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:focus, .btn-outline-warning.focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-warning.disabled, .btn-outline-warning:disabled {\n color: #ffc107;\n background-color: transparent;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,\n.show > .btn-outline-warning.dropdown-toggle {\n color: #212529;\n background-color: #ffc107;\n border-color: #ffc107;\n}\n\n.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-warning.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.btn-outline-danger {\n color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:hover {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:focus, .btn-outline-danger.focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-danger.disabled, .btn-outline-danger:disabled {\n color: #dc3545;\n background-color: transparent;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,\n.show > .btn-outline-danger.dropdown-toggle {\n color: #fff;\n background-color: #dc3545;\n border-color: #dc3545;\n}\n\n.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-danger.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.btn-outline-light {\n color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:hover {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:focus, .btn-outline-light.focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-light.disabled, .btn-outline-light:disabled {\n color: #f8f9fa;\n background-color: transparent;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,\n.show > .btn-outline-light.dropdown-toggle {\n color: #212529;\n background-color: #f8f9fa;\n border-color: #f8f9fa;\n}\n\n.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-light.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.btn-outline-dark {\n color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:hover {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:focus, .btn-outline-dark.focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-outline-dark.disabled, .btn-outline-dark:disabled {\n color: #343a40;\n background-color: transparent;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,\n.show > .btn-outline-dark.dropdown-toggle {\n color: #fff;\n background-color: #343a40;\n border-color: #343a40;\n}\n\n.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,\n.show > .btn-outline-dark.dropdown-toggle:focus {\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.btn-link {\n font-weight: 400;\n color: #007bff;\n text-decoration: none;\n}\n\n.btn-link:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\n.btn-link:focus, .btn-link.focus {\n text-decoration: underline;\n box-shadow: none;\n}\n\n.btn-link:disabled, .btn-link.disabled {\n color: #6c757d;\n pointer-events: none;\n}\n\n.btn-lg, .btn-group-lg > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.btn-sm, .btn-group-sm > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.btn-block {\n display: block;\n width: 100%;\n}\n\n.btn-block + .btn-block {\n margin-top: 0.5rem;\n}\n\ninput[type=\"submit\"].btn-block,\ninput[type=\"reset\"].btn-block,\ninput[type=\"button\"].btn-block {\n width: 100%;\n}\n\n.fade {\n transition: opacity 0.15s linear;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .fade {\n transition: none;\n }\n}\n\n.fade:not(.show) {\n opacity: 0;\n}\n\n.collapse:not(.show) {\n display: none;\n}\n\n.collapsing {\n position: relative;\n height: 0;\n overflow: hidden;\n transition: height 0.35s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .collapsing {\n transition: none;\n }\n}\n\n.dropup,\n.dropright,\n.dropdown,\n.dropleft {\n position: relative;\n}\n\n.dropdown-toggle {\n white-space: nowrap;\n}\n\n.dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid;\n border-right: 0.3em solid transparent;\n border-bottom: 0;\n border-left: 0.3em solid transparent;\n}\n\n.dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropdown-menu {\n position: absolute;\n top: 100%;\n left: 0;\n z-index: 1000;\n display: none;\n float: left;\n min-width: 10rem;\n padding: 0.5rem 0;\n margin: 0.125rem 0 0;\n font-size: 1rem;\n color: #212529;\n text-align: left;\n list-style: none;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 0.25rem;\n}\n\n.dropdown-menu-left {\n right: auto;\n left: 0;\n}\n\n.dropdown-menu-right {\n right: 0;\n left: auto;\n}\n\n@media (min-width: 576px) {\n .dropdown-menu-sm-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-sm-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 768px) {\n .dropdown-menu-md-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-md-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 992px) {\n .dropdown-menu-lg-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-lg-right {\n right: 0;\n left: auto;\n }\n}\n\n@media (min-width: 1200px) {\n .dropdown-menu-xl-left {\n right: auto;\n left: 0;\n }\n .dropdown-menu-xl-right {\n right: 0;\n left: auto;\n }\n}\n\n.dropup .dropdown-menu {\n top: auto;\n bottom: 100%;\n margin-top: 0;\n margin-bottom: 0.125rem;\n}\n\n.dropup .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0;\n border-right: 0.3em solid transparent;\n border-bottom: 0.3em solid;\n border-left: 0.3em solid transparent;\n}\n\n.dropup .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-menu {\n top: 0;\n right: auto;\n left: 100%;\n margin-top: 0;\n margin-left: 0.125rem;\n}\n\n.dropright .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0;\n border-bottom: 0.3em solid transparent;\n border-left: 0.3em solid;\n}\n\n.dropright .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropright .dropdown-toggle::after {\n vertical-align: 0;\n}\n\n.dropleft .dropdown-menu {\n top: 0;\n right: 100%;\n left: auto;\n margin-top: 0;\n margin-right: 0.125rem;\n}\n\n.dropleft .dropdown-toggle::after {\n display: inline-block;\n margin-left: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n}\n\n.dropleft .dropdown-toggle::after {\n display: none;\n}\n\n.dropleft .dropdown-toggle::before {\n display: inline-block;\n margin-right: 0.255em;\n vertical-align: 0.255em;\n content: \"\";\n border-top: 0.3em solid transparent;\n border-right: 0.3em solid;\n border-bottom: 0.3em solid transparent;\n}\n\n.dropleft .dropdown-toggle:empty::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle::before {\n vertical-align: 0;\n}\n\n.dropdown-menu[x-placement^=\"top\"], .dropdown-menu[x-placement^=\"right\"], .dropdown-menu[x-placement^=\"bottom\"], .dropdown-menu[x-placement^=\"left\"] {\n right: auto;\n bottom: auto;\n}\n\n.dropdown-divider {\n height: 0;\n margin: 0.5rem 0;\n overflow: hidden;\n border-top: 1px solid #e9ecef;\n}\n\n.dropdown-item {\n display: block;\n width: 100%;\n padding: 0.25rem 1.5rem;\n clear: both;\n font-weight: 400;\n color: #212529;\n text-align: inherit;\n white-space: nowrap;\n background-color: transparent;\n border: 0;\n}\n\n.dropdown-item:hover, .dropdown-item:focus {\n color: #16181b;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.dropdown-item.active, .dropdown-item:active {\n color: #fff;\n text-decoration: none;\n background-color: #007bff;\n}\n\n.dropdown-item.disabled, .dropdown-item:disabled {\n color: #6c757d;\n pointer-events: none;\n background-color: transparent;\n}\n\n.dropdown-menu.show {\n display: block;\n}\n\n.dropdown-header {\n display: block;\n padding: 0.5rem 1.5rem;\n margin-bottom: 0;\n font-size: 0.875rem;\n color: #6c757d;\n white-space: nowrap;\n}\n\n.dropdown-item-text {\n display: block;\n padding: 0.25rem 1.5rem;\n color: #212529;\n}\n\n.btn-group,\n.btn-group-vertical {\n position: relative;\n display: inline-flex;\n vertical-align: middle;\n}\n\n.btn-group > .btn,\n.btn-group-vertical > .btn {\n position: relative;\n flex: 1 1 auto;\n}\n\n.btn-group > .btn:hover,\n.btn-group-vertical > .btn:hover {\n z-index: 1;\n}\n\n.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,\n.btn-group-vertical > .btn:focus,\n.btn-group-vertical > .btn:active,\n.btn-group-vertical > .btn.active {\n z-index: 1;\n}\n\n.btn-toolbar {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n}\n\n.btn-toolbar .input-group {\n width: auto;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) {\n margin-left: -1px;\n}\n\n.btn-group > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group > .btn-group:not(:last-child) > .btn {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.btn-group > .btn:not(:first-child),\n.btn-group > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.dropdown-toggle-split {\n padding-right: 0.5625rem;\n padding-left: 0.5625rem;\n}\n\n.dropdown-toggle-split::after,\n.dropup .dropdown-toggle-split::after,\n.dropright .dropdown-toggle-split::after {\n margin-left: 0;\n}\n\n.dropleft .dropdown-toggle-split::before {\n margin-right: 0;\n}\n\n.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {\n padding-right: 0.375rem;\n padding-left: 0.375rem;\n}\n\n.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {\n padding-right: 0.75rem;\n padding-left: 0.75rem;\n}\n\n.btn-group-vertical {\n flex-direction: column;\n align-items: flex-start;\n justify-content: center;\n}\n\n.btn-group-vertical > .btn,\n.btn-group-vertical > .btn-group {\n width: 100%;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) {\n margin-top: -1px;\n}\n\n.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),\n.btn-group-vertical > .btn-group:not(:last-child) > .btn {\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.btn-group-vertical > .btn:not(:first-child),\n.btn-group-vertical > .btn-group:not(:first-child) > .btn {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.btn-group-toggle > .btn,\n.btn-group-toggle > .btn-group > .btn {\n margin-bottom: 0;\n}\n\n.btn-group-toggle > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn input[type=\"checkbox\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"radio\"],\n.btn-group-toggle > .btn-group > .btn input[type=\"checkbox\"] {\n position: absolute;\n clip: rect(0, 0, 0, 0);\n pointer-events: none;\n}\n\n.input-group {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: stretch;\n width: 100%;\n}\n\n.input-group > .form-control,\n.input-group > .form-control-plaintext,\n.input-group > .custom-select,\n.input-group > .custom-file {\n position: relative;\n flex: 1 1 auto;\n width: 1%;\n margin-bottom: 0;\n}\n\n.input-group > .form-control + .form-control,\n.input-group > .form-control + .custom-select,\n.input-group > .form-control + .custom-file,\n.input-group > .form-control-plaintext + .form-control,\n.input-group > .form-control-plaintext + .custom-select,\n.input-group > .form-control-plaintext + .custom-file,\n.input-group > .custom-select + .form-control,\n.input-group > .custom-select + .custom-select,\n.input-group > .custom-select + .custom-file,\n.input-group > .custom-file + .form-control,\n.input-group > .custom-file + .custom-select,\n.input-group > .custom-file + .custom-file {\n margin-left: -1px;\n}\n\n.input-group > .form-control:focus,\n.input-group > .custom-select:focus,\n.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {\n z-index: 3;\n}\n\n.input-group > .custom-file .custom-file-input:focus {\n z-index: 4;\n}\n\n.input-group > .form-control:not(:last-child),\n.input-group > .custom-select:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .form-control:not(:first-child),\n.input-group > .custom-select:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group > .custom-file {\n display: flex;\n align-items: center;\n}\n\n.input-group > .custom-file:not(:last-child) .custom-file-label,\n.input-group > .custom-file:not(:last-child) .custom-file-label::after {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .custom-file:not(:first-child) .custom-file-label {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.input-group-prepend,\n.input-group-append {\n display: flex;\n}\n\n.input-group-prepend .btn,\n.input-group-append .btn {\n position: relative;\n z-index: 2;\n}\n\n.input-group-prepend .btn:focus,\n.input-group-append .btn:focus {\n z-index: 3;\n}\n\n.input-group-prepend .btn + .btn,\n.input-group-prepend .btn + .input-group-text,\n.input-group-prepend .input-group-text + .input-group-text,\n.input-group-prepend .input-group-text + .btn,\n.input-group-append .btn + .btn,\n.input-group-append .btn + .input-group-text,\n.input-group-append .input-group-text + .input-group-text,\n.input-group-append .input-group-text + .btn {\n margin-left: -1px;\n}\n\n.input-group-prepend {\n margin-right: -1px;\n}\n\n.input-group-append {\n margin-left: -1px;\n}\n\n.input-group-text {\n display: flex;\n align-items: center;\n padding: 0.375rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n text-align: center;\n white-space: nowrap;\n background-color: #e9ecef;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.input-group-text input[type=\"radio\"],\n.input-group-text input[type=\"checkbox\"] {\n margin-top: 0;\n}\n\n.input-group-lg > .form-control:not(textarea),\n.input-group-lg > .custom-select {\n height: calc(1.5em + 1rem + 2px);\n}\n\n.input-group-lg > .form-control,\n.input-group-lg > .custom-select,\n.input-group-lg > .input-group-prepend > .input-group-text,\n.input-group-lg > .input-group-append > .input-group-text,\n.input-group-lg > .input-group-prepend > .btn,\n.input-group-lg > .input-group-append > .btn {\n padding: 0.5rem 1rem;\n font-size: 1.25rem;\n line-height: 1.5;\n border-radius: 0.3rem;\n}\n\n.input-group-sm > .form-control:not(textarea),\n.input-group-sm > .custom-select {\n height: calc(1.5em + 0.5rem + 2px);\n}\n\n.input-group-sm > .form-control,\n.input-group-sm > .custom-select,\n.input-group-sm > .input-group-prepend > .input-group-text,\n.input-group-sm > .input-group-append > .input-group-text,\n.input-group-sm > .input-group-prepend > .btn,\n.input-group-sm > .input-group-append > .btn {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n border-radius: 0.2rem;\n}\n\n.input-group-lg > .custom-select,\n.input-group-sm > .custom-select {\n padding-right: 1.75rem;\n}\n\n.input-group > .input-group-prepend > .btn,\n.input-group > .input-group-prepend > .input-group-text,\n.input-group > .input-group-append:not(:last-child) > .btn,\n.input-group > .input-group-append:not(:last-child) > .input-group-text,\n.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),\n.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\n.input-group > .input-group-append > .btn,\n.input-group > .input-group-append > .input-group-text,\n.input-group > .input-group-prepend:not(:first-child) > .btn,\n.input-group > .input-group-prepend:not(:first-child) > .input-group-text,\n.input-group > .input-group-prepend:first-child > .btn:not(:first-child),\n.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.custom-control {\n position: relative;\n display: block;\n min-height: 1.5rem;\n padding-left: 1.5rem;\n}\n\n.custom-control-inline {\n display: inline-flex;\n margin-right: 1rem;\n}\n\n.custom-control-input {\n position: absolute;\n z-index: -1;\n opacity: 0;\n}\n\n.custom-control-input:checked ~ .custom-control-label::before {\n color: #fff;\n border-color: #007bff;\n background-color: #007bff;\n}\n\n.custom-control-input:focus ~ .custom-control-label::before {\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {\n border-color: #80bdff;\n}\n\n.custom-control-input:not(:disabled):active ~ .custom-control-label::before {\n color: #fff;\n background-color: #b3d7ff;\n border-color: #b3d7ff;\n}\n\n.custom-control-input:disabled ~ .custom-control-label {\n color: #6c757d;\n}\n\n.custom-control-input:disabled ~ .custom-control-label::before {\n background-color: #e9ecef;\n}\n\n.custom-control-label {\n position: relative;\n margin-bottom: 0;\n vertical-align: top;\n}\n\n.custom-control-label::before {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n pointer-events: none;\n content: \"\";\n background-color: #fff;\n border: #adb5bd solid 1px;\n}\n\n.custom-control-label::after {\n position: absolute;\n top: 0.25rem;\n left: -1.5rem;\n display: block;\n width: 1rem;\n height: 1rem;\n content: \"\";\n background: no-repeat 50% / 50% 50%;\n}\n\n.custom-checkbox .custom-control-label::before {\n border-radius: 0.25rem;\n}\n\n.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3e%3c/svg%3e\");\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {\n border-color: #007bff;\n background-color: #007bff;\n}\n\n.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e\");\n}\n\n.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-radio .custom-control-label::before {\n border-radius: 50%;\n}\n\n.custom-radio .custom-control-input:checked ~ .custom-control-label::after {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e\");\n}\n\n.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-switch {\n padding-left: 2.25rem;\n}\n\n.custom-switch .custom-control-label::before {\n left: -2.25rem;\n width: 1.75rem;\n pointer-events: all;\n border-radius: 0.5rem;\n}\n\n.custom-switch .custom-control-label::after {\n top: calc(0.25rem + 2px);\n left: calc(-2.25rem + 2px);\n width: calc(1rem - 4px);\n height: calc(1rem - 4px);\n background-color: #adb5bd;\n border-radius: 0.5rem;\n transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-switch .custom-control-label::after {\n transition: none;\n }\n}\n\n.custom-switch .custom-control-input:checked ~ .custom-control-label::after {\n background-color: #fff;\n transform: translateX(0.75rem);\n}\n\n.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {\n background-color: rgba(0, 123, 255, 0.5);\n}\n\n.custom-select {\n display: inline-block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 1.75rem 0.375rem 0.75rem;\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n vertical-align: middle;\n background: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e\") no-repeat right 0.75rem center/8px 10px;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n appearance: none;\n}\n\n.custom-select:focus {\n border-color: #80bdff;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-select:focus::-ms-value {\n color: #495057;\n background-color: #fff;\n}\n\n.custom-select[multiple], .custom-select[size]:not([size=\"1\"]) {\n height: auto;\n padding-right: 0.75rem;\n background-image: none;\n}\n\n.custom-select:disabled {\n color: #6c757d;\n background-color: #e9ecef;\n}\n\n.custom-select::-ms-expand {\n display: none;\n}\n\n.custom-select-sm {\n height: calc(1.5em + 0.5rem + 2px);\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n padding-left: 0.5rem;\n font-size: 0.875rem;\n}\n\n.custom-select-lg {\n height: calc(1.5em + 1rem + 2px);\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n padding-left: 1rem;\n font-size: 1.25rem;\n}\n\n.custom-file {\n position: relative;\n display: inline-block;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n margin-bottom: 0;\n}\n\n.custom-file-input {\n position: relative;\n z-index: 2;\n width: 100%;\n height: calc(1.5em + 0.75rem + 2px);\n margin: 0;\n opacity: 0;\n}\n\n.custom-file-input:focus ~ .custom-file-label {\n border-color: #80bdff;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-file-input:disabled ~ .custom-file-label {\n background-color: #e9ecef;\n}\n\n.custom-file-input:lang(en) ~ .custom-file-label::after {\n content: \"Browse\";\n}\n\n.custom-file-input ~ .custom-file-label[data-browse]::after {\n content: attr(data-browse);\n}\n\n.custom-file-label {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n height: calc(1.5em + 0.75rem + 2px);\n padding: 0.375rem 0.75rem;\n font-weight: 400;\n line-height: 1.5;\n color: #495057;\n background-color: #fff;\n border: 1px solid #ced4da;\n border-radius: 0.25rem;\n}\n\n.custom-file-label::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n z-index: 3;\n display: block;\n height: calc(1.5em + 0.75rem);\n padding: 0.375rem 0.75rem;\n line-height: 1.5;\n color: #495057;\n content: \"Browse\";\n background-color: #e9ecef;\n border-left: inherit;\n border-radius: 0 0.25rem 0.25rem 0;\n}\n\n.custom-range {\n width: 100%;\n height: calc(1rem + 0.4rem);\n padding: 0;\n background-color: transparent;\n appearance: none;\n}\n\n.custom-range:focus {\n outline: none;\n}\n\n.custom-range:focus::-webkit-slider-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-moz-range-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range:focus::-ms-thumb {\n box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.custom-range::-moz-focus-outer {\n border: 0;\n}\n\n.custom-range::-webkit-slider-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: -0.25rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-webkit-slider-thumb {\n transition: none;\n }\n}\n\n.custom-range::-webkit-slider-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-webkit-slider-runnable-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-moz-range-thumb {\n width: 1rem;\n height: 1rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-moz-range-thumb {\n transition: none;\n }\n}\n\n.custom-range::-moz-range-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-moz-range-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: #dee2e6;\n border-color: transparent;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-thumb {\n width: 1rem;\n height: 1rem;\n margin-top: 0;\n margin-right: 0.2rem;\n margin-left: 0.2rem;\n background-color: #007bff;\n border: 0;\n border-radius: 1rem;\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n appearance: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-range::-ms-thumb {\n transition: none;\n }\n}\n\n.custom-range::-ms-thumb:active {\n background-color: #b3d7ff;\n}\n\n.custom-range::-ms-track {\n width: 100%;\n height: 0.5rem;\n color: transparent;\n cursor: pointer;\n background-color: transparent;\n border-color: transparent;\n border-width: 0.5rem;\n}\n\n.custom-range::-ms-fill-lower {\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range::-ms-fill-upper {\n margin-right: 15px;\n background-color: #dee2e6;\n border-radius: 1rem;\n}\n\n.custom-range:disabled::-webkit-slider-thumb {\n background-color: #adb5bd;\n}\n\n.custom-range:disabled::-webkit-slider-runnable-track {\n cursor: default;\n}\n\n.custom-range:disabled::-moz-range-thumb {\n background-color: #adb5bd;\n}\n\n.custom-range:disabled::-moz-range-track {\n cursor: default;\n}\n\n.custom-range:disabled::-ms-thumb {\n background-color: #adb5bd;\n}\n\n.custom-control-label::before,\n.custom-file-label,\n.custom-select {\n transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .custom-control-label::before,\n .custom-file-label,\n .custom-select {\n transition: none;\n }\n}\n\n.nav {\n display: flex;\n flex-wrap: wrap;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.nav-link {\n display: block;\n padding: 0.5rem 1rem;\n}\n\n.nav-link:hover, .nav-link:focus {\n text-decoration: none;\n}\n\n.nav-link.disabled {\n color: #6c757d;\n pointer-events: none;\n cursor: default;\n}\n\n.nav-tabs {\n border-bottom: 1px solid #dee2e6;\n}\n\n.nav-tabs .nav-item {\n margin-bottom: -1px;\n}\n\n.nav-tabs .nav-link {\n border: 1px solid transparent;\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {\n border-color: #e9ecef #e9ecef #dee2e6;\n}\n\n.nav-tabs .nav-link.disabled {\n color: #6c757d;\n background-color: transparent;\n border-color: transparent;\n}\n\n.nav-tabs .nav-link.active,\n.nav-tabs .nav-item.show .nav-link {\n color: #495057;\n background-color: #fff;\n border-color: #dee2e6 #dee2e6 #fff;\n}\n\n.nav-tabs .dropdown-menu {\n margin-top: -1px;\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.nav-pills .nav-link {\n border-radius: 0.25rem;\n}\n\n.nav-pills .nav-link.active,\n.nav-pills .show > .nav-link {\n color: #fff;\n background-color: #007bff;\n}\n\n.nav-fill .nav-item {\n flex: 1 1 auto;\n text-align: center;\n}\n\n.nav-justified .nav-item {\n flex-basis: 0;\n flex-grow: 1;\n text-align: center;\n}\n\n.tab-content > .tab-pane {\n display: none;\n}\n\n.tab-content > .active {\n display: block;\n}\n\n.navbar {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 0.5rem 1rem;\n}\n\n.navbar > .container,\n.navbar > .container-fluid {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n}\n\n.navbar-brand {\n display: inline-block;\n padding-top: 0.3125rem;\n padding-bottom: 0.3125rem;\n margin-right: 1rem;\n font-size: 1.25rem;\n line-height: inherit;\n white-space: nowrap;\n}\n\n.navbar-brand:hover, .navbar-brand:focus {\n text-decoration: none;\n}\n\n.navbar-nav {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n list-style: none;\n}\n\n.navbar-nav .nav-link {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-nav .dropdown-menu {\n position: static;\n float: none;\n}\n\n.navbar-text {\n display: inline-block;\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n\n.navbar-collapse {\n flex-basis: 100%;\n flex-grow: 1;\n align-items: center;\n}\n\n.navbar-toggler {\n padding: 0.25rem 0.75rem;\n font-size: 1.25rem;\n line-height: 1;\n background-color: transparent;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.navbar-toggler:hover, .navbar-toggler:focus {\n text-decoration: none;\n}\n\n.navbar-toggler-icon {\n display: inline-block;\n width: 1.5em;\n height: 1.5em;\n vertical-align: middle;\n content: \"\";\n background: no-repeat center center;\n background-size: 100% 100%;\n}\n\n@media (max-width: 575.98px) {\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 576px) {\n .navbar-expand-sm {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-sm .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-sm .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-sm .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-sm > .container,\n .navbar-expand-sm > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-sm .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-sm .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 767.98px) {\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 768px) {\n .navbar-expand-md {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-md .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-md .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-md .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-md > .container,\n .navbar-expand-md > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-md .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-md .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 991.98px) {\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 992px) {\n .navbar-expand-lg {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-lg .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-lg .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-lg .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-lg > .container,\n .navbar-expand-lg > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-lg .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-lg .navbar-toggler {\n display: none;\n }\n}\n\n@media (max-width: 1199.98px) {\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .navbar-expand-xl {\n flex-flow: row nowrap;\n justify-content: flex-start;\n }\n .navbar-expand-xl .navbar-nav {\n flex-direction: row;\n }\n .navbar-expand-xl .navbar-nav .dropdown-menu {\n position: absolute;\n }\n .navbar-expand-xl .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n }\n .navbar-expand-xl > .container,\n .navbar-expand-xl > .container-fluid {\n flex-wrap: nowrap;\n }\n .navbar-expand-xl .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n }\n .navbar-expand-xl .navbar-toggler {\n display: none;\n }\n}\n\n.navbar-expand {\n flex-flow: row nowrap;\n justify-content: flex-start;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n padding-right: 0;\n padding-left: 0;\n}\n\n.navbar-expand .navbar-nav {\n flex-direction: row;\n}\n\n.navbar-expand .navbar-nav .dropdown-menu {\n position: absolute;\n}\n\n.navbar-expand .navbar-nav .nav-link {\n padding-right: 0.5rem;\n padding-left: 0.5rem;\n}\n\n.navbar-expand > .container,\n.navbar-expand > .container-fluid {\n flex-wrap: nowrap;\n}\n\n.navbar-expand .navbar-collapse {\n display: flex !important;\n flex-basis: auto;\n}\n\n.navbar-expand .navbar-toggler {\n display: none;\n}\n\n.navbar-light .navbar-brand {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-nav .nav-link {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {\n color: rgba(0, 0, 0, 0.7);\n}\n\n.navbar-light .navbar-nav .nav-link.disabled {\n color: rgba(0, 0, 0, 0.3);\n}\n\n.navbar-light .navbar-nav .show > .nav-link,\n.navbar-light .navbar-nav .active > .nav-link,\n.navbar-light .navbar-nav .nav-link.show,\n.navbar-light .navbar-nav .nav-link.active {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-toggler {\n color: rgba(0, 0, 0, 0.5);\n border-color: rgba(0, 0, 0, 0.1);\n}\n\n.navbar-light .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.navbar-light .navbar-text {\n color: rgba(0, 0, 0, 0.5);\n}\n\n.navbar-light .navbar-text a {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {\n color: rgba(0, 0, 0, 0.9);\n}\n\n.navbar-dark .navbar-brand {\n color: #fff;\n}\n\n.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {\n color: #fff;\n}\n\n.navbar-dark .navbar-nav .nav-link {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {\n color: rgba(255, 255, 255, 0.75);\n}\n\n.navbar-dark .navbar-nav .nav-link.disabled {\n color: rgba(255, 255, 255, 0.25);\n}\n\n.navbar-dark .navbar-nav .show > .nav-link,\n.navbar-dark .navbar-nav .active > .nav-link,\n.navbar-dark .navbar-nav .nav-link.show,\n.navbar-dark .navbar-nav .nav-link.active {\n color: #fff;\n}\n\n.navbar-dark .navbar-toggler {\n color: rgba(255, 255, 255, 0.5);\n border-color: rgba(255, 255, 255, 0.1);\n}\n\n.navbar-dark .navbar-toggler-icon {\n background-image: url(\"data:image/svg+xml,%3csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e\");\n}\n\n.navbar-dark .navbar-text {\n color: rgba(255, 255, 255, 0.5);\n}\n\n.navbar-dark .navbar-text a {\n color: #fff;\n}\n\n.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {\n color: #fff;\n}\n\n.card {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: border-box;\n border: 1px solid rgba(0, 0, 0, 0.125);\n border-radius: 0.25rem;\n}\n\n.card > hr {\n margin-right: 0;\n margin-left: 0;\n}\n\n.card > .list-group:first-child .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.card > .list-group:last-child .list-group-item:last-child {\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.card-body {\n flex: 1 1 auto;\n padding: 1.25rem;\n}\n\n.card-title {\n margin-bottom: 0.75rem;\n}\n\n.card-subtitle {\n margin-top: -0.375rem;\n margin-bottom: 0;\n}\n\n.card-text:last-child {\n margin-bottom: 0;\n}\n\n.card-link:hover {\n text-decoration: none;\n}\n\n.card-link + .card-link {\n margin-left: 1.25rem;\n}\n\n.card-header {\n padding: 0.75rem 1.25rem;\n margin-bottom: 0;\n background-color: rgba(0, 0, 0, 0.03);\n border-bottom: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-header:first-child {\n border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;\n}\n\n.card-header + .list-group .list-group-item:first-child {\n border-top: 0;\n}\n\n.card-footer {\n padding: 0.75rem 1.25rem;\n background-color: rgba(0, 0, 0, 0.03);\n border-top: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.card-footer:last-child {\n border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);\n}\n\n.card-header-tabs {\n margin-right: -0.625rem;\n margin-bottom: -0.75rem;\n margin-left: -0.625rem;\n border-bottom: 0;\n}\n\n.card-header-pills {\n margin-right: -0.625rem;\n margin-left: -0.625rem;\n}\n\n.card-img-overlay {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n padding: 1.25rem;\n}\n\n.card-img {\n width: 100%;\n border-radius: calc(0.25rem - 1px);\n}\n\n.card-img-top {\n width: 100%;\n border-top-left-radius: calc(0.25rem - 1px);\n border-top-right-radius: calc(0.25rem - 1px);\n}\n\n.card-img-bottom {\n width: 100%;\n border-bottom-right-radius: calc(0.25rem - 1px);\n border-bottom-left-radius: calc(0.25rem - 1px);\n}\n\n.card-deck {\n display: flex;\n flex-direction: column;\n}\n\n.card-deck .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-deck {\n flex-flow: row wrap;\n margin-right: -15px;\n margin-left: -15px;\n }\n .card-deck .card {\n display: flex;\n flex: 1 0 0%;\n flex-direction: column;\n margin-right: 15px;\n margin-bottom: 0;\n margin-left: 15px;\n }\n}\n\n.card-group {\n display: flex;\n flex-direction: column;\n}\n\n.card-group > .card {\n margin-bottom: 15px;\n}\n\n@media (min-width: 576px) {\n .card-group {\n flex-flow: row wrap;\n }\n .card-group > .card {\n flex: 1 0 0%;\n margin-bottom: 0;\n }\n .card-group > .card + .card {\n margin-left: 0;\n border-left: 0;\n }\n .card-group > .card:not(:last-child) {\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-top,\n .card-group > .card:not(:last-child) .card-header {\n border-top-right-radius: 0;\n }\n .card-group > .card:not(:last-child) .card-img-bottom,\n .card-group > .card:not(:last-child) .card-footer {\n border-bottom-right-radius: 0;\n }\n .card-group > .card:not(:first-child) {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-top,\n .card-group > .card:not(:first-child) .card-header {\n border-top-left-radius: 0;\n }\n .card-group > .card:not(:first-child) .card-img-bottom,\n .card-group > .card:not(:first-child) .card-footer {\n border-bottom-left-radius: 0;\n }\n}\n\n.card-columns .card {\n margin-bottom: 0.75rem;\n}\n\n@media (min-width: 576px) {\n .card-columns {\n column-count: 3;\n column-gap: 1.25rem;\n orphans: 1;\n widows: 1;\n }\n .card-columns .card {\n display: inline-block;\n width: 100%;\n }\n}\n\n.accordion > .card {\n overflow: hidden;\n}\n\n.accordion > .card:not(:first-of-type) .card-header:first-child {\n border-radius: 0;\n}\n\n.accordion > .card:not(:first-of-type):not(:last-of-type) {\n border-bottom: 0;\n border-radius: 0;\n}\n\n.accordion > .card:first-of-type {\n border-bottom: 0;\n border-bottom-right-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n.accordion > .card:last-of-type {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n}\n\n.accordion > .card .card-header {\n margin-bottom: -1px;\n}\n\n.breadcrumb {\n display: flex;\n flex-wrap: wrap;\n padding: 0.75rem 1rem;\n margin-bottom: 1rem;\n list-style: none;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.breadcrumb-item + .breadcrumb-item {\n padding-left: 0.5rem;\n}\n\n.breadcrumb-item + .breadcrumb-item::before {\n display: inline-block;\n padding-right: 0.5rem;\n color: #6c757d;\n content: \"/\";\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: underline;\n}\n\n.breadcrumb-item + .breadcrumb-item:hover::before {\n text-decoration: none;\n}\n\n.breadcrumb-item.active {\n color: #6c757d;\n}\n\n.pagination {\n display: flex;\n padding-left: 0;\n list-style: none;\n border-radius: 0.25rem;\n}\n\n.page-link {\n position: relative;\n display: block;\n padding: 0.5rem 0.75rem;\n margin-left: -1px;\n line-height: 1.25;\n color: #007bff;\n background-color: #fff;\n border: 1px solid #dee2e6;\n}\n\n.page-link:hover {\n z-index: 2;\n color: #0056b3;\n text-decoration: none;\n background-color: #e9ecef;\n border-color: #dee2e6;\n}\n\n.page-link:focus {\n z-index: 2;\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);\n}\n\n.page-item:first-child .page-link {\n margin-left: 0;\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.page-item:last-child .page-link {\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n}\n\n.page-item.active .page-link {\n z-index: 1;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.page-item.disabled .page-link {\n color: #6c757d;\n pointer-events: none;\n cursor: auto;\n background-color: #fff;\n border-color: #dee2e6;\n}\n\n.pagination-lg .page-link {\n padding: 0.75rem 1.5rem;\n font-size: 1.25rem;\n line-height: 1.5;\n}\n\n.pagination-lg .page-item:first-child .page-link {\n border-top-left-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.pagination-lg .page-item:last-child .page-link {\n border-top-right-radius: 0.3rem;\n border-bottom-right-radius: 0.3rem;\n}\n\n.pagination-sm .page-link {\n padding: 0.25rem 0.5rem;\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n.pagination-sm .page-item:first-child .page-link {\n border-top-left-radius: 0.2rem;\n border-bottom-left-radius: 0.2rem;\n}\n\n.pagination-sm .page-item:last-child .page-link {\n border-top-right-radius: 0.2rem;\n border-bottom-right-radius: 0.2rem;\n}\n\n.badge {\n display: inline-block;\n padding: 0.25em 0.4em;\n font-size: 75%;\n font-weight: 700;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n vertical-align: baseline;\n border-radius: 0.25rem;\n transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .badge {\n transition: none;\n }\n}\n\na.badge:hover, a.badge:focus {\n text-decoration: none;\n}\n\n.badge:empty {\n display: none;\n}\n\n.btn .badge {\n position: relative;\n top: -1px;\n}\n\n.badge-pill {\n padding-right: 0.6em;\n padding-left: 0.6em;\n border-radius: 10rem;\n}\n\n.badge-primary {\n color: #fff;\n background-color: #007bff;\n}\n\na.badge-primary:hover, a.badge-primary:focus {\n color: #fff;\n background-color: #0062cc;\n}\n\na.badge-primary:focus, a.badge-primary.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);\n}\n\n.badge-secondary {\n color: #fff;\n background-color: #6c757d;\n}\n\na.badge-secondary:hover, a.badge-secondary:focus {\n color: #fff;\n background-color: #545b62;\n}\n\na.badge-secondary:focus, a.badge-secondary.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);\n}\n\n.badge-success {\n color: #fff;\n background-color: #28a745;\n}\n\na.badge-success:hover, a.badge-success:focus {\n color: #fff;\n background-color: #1e7e34;\n}\n\na.badge-success:focus, a.badge-success.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);\n}\n\n.badge-info {\n color: #fff;\n background-color: #17a2b8;\n}\n\na.badge-info:hover, a.badge-info:focus {\n color: #fff;\n background-color: #117a8b;\n}\n\na.badge-info:focus, a.badge-info.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);\n}\n\n.badge-warning {\n color: #212529;\n background-color: #ffc107;\n}\n\na.badge-warning:hover, a.badge-warning:focus {\n color: #212529;\n background-color: #d39e00;\n}\n\na.badge-warning:focus, a.badge-warning.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);\n}\n\n.badge-danger {\n color: #fff;\n background-color: #dc3545;\n}\n\na.badge-danger:hover, a.badge-danger:focus {\n color: #fff;\n background-color: #bd2130;\n}\n\na.badge-danger:focus, a.badge-danger.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);\n}\n\n.badge-light {\n color: #212529;\n background-color: #f8f9fa;\n}\n\na.badge-light:hover, a.badge-light:focus {\n color: #212529;\n background-color: #dae0e5;\n}\n\na.badge-light:focus, a.badge-light.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);\n}\n\n.badge-dark {\n color: #fff;\n background-color: #343a40;\n}\n\na.badge-dark:hover, a.badge-dark:focus {\n color: #fff;\n background-color: #1d2124;\n}\n\na.badge-dark:focus, a.badge-dark.focus {\n outline: 0;\n box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);\n}\n\n.jumbotron {\n padding: 2rem 1rem;\n margin-bottom: 2rem;\n background-color: #e9ecef;\n border-radius: 0.3rem;\n}\n\n@media (min-width: 576px) {\n .jumbotron {\n padding: 4rem 2rem;\n }\n}\n\n.jumbotron-fluid {\n padding-right: 0;\n padding-left: 0;\n border-radius: 0;\n}\n\n.alert {\n position: relative;\n padding: 0.75rem 1.25rem;\n margin-bottom: 1rem;\n border: 1px solid transparent;\n border-radius: 0.25rem;\n}\n\n.alert-heading {\n color: inherit;\n}\n\n.alert-link {\n font-weight: 700;\n}\n\n.alert-dismissible {\n padding-right: 4rem;\n}\n\n.alert-dismissible .close {\n position: absolute;\n top: 0;\n right: 0;\n padding: 0.75rem 1.25rem;\n color: inherit;\n}\n\n.alert-primary {\n color: #004085;\n background-color: #cce5ff;\n border-color: #b8daff;\n}\n\n.alert-primary hr {\n border-top-color: #9fcdff;\n}\n\n.alert-primary .alert-link {\n color: #002752;\n}\n\n.alert-secondary {\n color: #383d41;\n background-color: #e2e3e5;\n border-color: #d6d8db;\n}\n\n.alert-secondary hr {\n border-top-color: #c8cbcf;\n}\n\n.alert-secondary .alert-link {\n color: #202326;\n}\n\n.alert-success {\n color: #155724;\n background-color: #d4edda;\n border-color: #c3e6cb;\n}\n\n.alert-success hr {\n border-top-color: #b1dfbb;\n}\n\n.alert-success .alert-link {\n color: #0b2e13;\n}\n\n.alert-info {\n color: #0c5460;\n background-color: #d1ecf1;\n border-color: #bee5eb;\n}\n\n.alert-info hr {\n border-top-color: #abdde5;\n}\n\n.alert-info .alert-link {\n color: #062c33;\n}\n\n.alert-warning {\n color: #856404;\n background-color: #fff3cd;\n border-color: #ffeeba;\n}\n\n.alert-warning hr {\n border-top-color: #ffe8a1;\n}\n\n.alert-warning .alert-link {\n color: #533f03;\n}\n\n.alert-danger {\n color: #721c24;\n background-color: #f8d7da;\n border-color: #f5c6cb;\n}\n\n.alert-danger hr {\n border-top-color: #f1b0b7;\n}\n\n.alert-danger .alert-link {\n color: #491217;\n}\n\n.alert-light {\n color: #818182;\n background-color: #fefefe;\n border-color: #fdfdfe;\n}\n\n.alert-light hr {\n border-top-color: #ececf6;\n}\n\n.alert-light .alert-link {\n color: #686868;\n}\n\n.alert-dark {\n color: #1b1e21;\n background-color: #d6d8d9;\n border-color: #c6c8ca;\n}\n\n.alert-dark hr {\n border-top-color: #b9bbbe;\n}\n\n.alert-dark .alert-link {\n color: #040505;\n}\n\n@keyframes progress-bar-stripes {\n from {\n background-position: 1rem 0;\n }\n to {\n background-position: 0 0;\n }\n}\n\n.progress {\n display: flex;\n height: 1rem;\n overflow: hidden;\n font-size: 0.75rem;\n background-color: #e9ecef;\n border-radius: 0.25rem;\n}\n\n.progress-bar {\n display: flex;\n flex-direction: column;\n justify-content: center;\n color: #fff;\n text-align: center;\n white-space: nowrap;\n background-color: #007bff;\n transition: width 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-bar {\n transition: none;\n }\n}\n\n.progress-bar-striped {\n background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);\n background-size: 1rem 1rem;\n}\n\n.progress-bar-animated {\n animation: progress-bar-stripes 1s linear infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .progress-bar-animated {\n animation: none;\n }\n}\n\n.media {\n display: flex;\n align-items: flex-start;\n}\n\n.media-body {\n flex: 1;\n}\n\n.list-group {\n display: flex;\n flex-direction: column;\n padding-left: 0;\n margin-bottom: 0;\n}\n\n.list-group-item-action {\n width: 100%;\n color: #495057;\n text-align: inherit;\n}\n\n.list-group-item-action:hover, .list-group-item-action:focus {\n z-index: 1;\n color: #495057;\n text-decoration: none;\n background-color: #f8f9fa;\n}\n\n.list-group-item-action:active {\n color: #212529;\n background-color: #e9ecef;\n}\n\n.list-group-item {\n position: relative;\n display: block;\n padding: 0.75rem 1.25rem;\n margin-bottom: -1px;\n background-color: #fff;\n border: 1px solid rgba(0, 0, 0, 0.125);\n}\n\n.list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n\n.list-group-item:last-child {\n margin-bottom: 0;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n}\n\n.list-group-item.disabled, .list-group-item:disabled {\n color: #6c757d;\n pointer-events: none;\n background-color: #fff;\n}\n\n.list-group-item.active {\n z-index: 2;\n color: #fff;\n background-color: #007bff;\n border-color: #007bff;\n}\n\n.list-group-horizontal {\n flex-direction: row;\n}\n\n.list-group-horizontal .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n}\n\n.list-group-horizontal .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n}\n\n.list-group-horizontal .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n}\n\n@media (min-width: 576px) {\n .list-group-horizontal-sm {\n flex-direction: row;\n }\n .list-group-horizontal-sm .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-sm .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-sm .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n@media (min-width: 768px) {\n .list-group-horizontal-md {\n flex-direction: row;\n }\n .list-group-horizontal-md .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-md .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-md .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n@media (min-width: 992px) {\n .list-group-horizontal-lg {\n flex-direction: row;\n }\n .list-group-horizontal-lg .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-lg .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-lg .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n@media (min-width: 1200px) {\n .list-group-horizontal-xl {\n flex-direction: row;\n }\n .list-group-horizontal-xl .list-group-item {\n margin-right: -1px;\n margin-bottom: 0;\n }\n .list-group-horizontal-xl .list-group-item:first-child {\n border-top-left-radius: 0.25rem;\n border-bottom-left-radius: 0.25rem;\n border-top-right-radius: 0;\n }\n .list-group-horizontal-xl .list-group-item:last-child {\n margin-right: 0;\n border-top-right-radius: 0.25rem;\n border-bottom-right-radius: 0.25rem;\n border-bottom-left-radius: 0;\n }\n}\n\n.list-group-flush .list-group-item {\n border-right: 0;\n border-left: 0;\n border-radius: 0;\n}\n\n.list-group-flush .list-group-item:last-child {\n margin-bottom: -1px;\n}\n\n.list-group-flush:first-child .list-group-item:first-child {\n border-top: 0;\n}\n\n.list-group-flush:last-child .list-group-item:last-child {\n margin-bottom: 0;\n border-bottom: 0;\n}\n\n.list-group-item-primary {\n color: #004085;\n background-color: #b8daff;\n}\n\n.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {\n color: #004085;\n background-color: #9fcdff;\n}\n\n.list-group-item-primary.list-group-item-action.active {\n color: #fff;\n background-color: #004085;\n border-color: #004085;\n}\n\n.list-group-item-secondary {\n color: #383d41;\n background-color: #d6d8db;\n}\n\n.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {\n color: #383d41;\n background-color: #c8cbcf;\n}\n\n.list-group-item-secondary.list-group-item-action.active {\n color: #fff;\n background-color: #383d41;\n border-color: #383d41;\n}\n\n.list-group-item-success {\n color: #155724;\n background-color: #c3e6cb;\n}\n\n.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {\n color: #155724;\n background-color: #b1dfbb;\n}\n\n.list-group-item-success.list-group-item-action.active {\n color: #fff;\n background-color: #155724;\n border-color: #155724;\n}\n\n.list-group-item-info {\n color: #0c5460;\n background-color: #bee5eb;\n}\n\n.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {\n color: #0c5460;\n background-color: #abdde5;\n}\n\n.list-group-item-info.list-group-item-action.active {\n color: #fff;\n background-color: #0c5460;\n border-color: #0c5460;\n}\n\n.list-group-item-warning {\n color: #856404;\n background-color: #ffeeba;\n}\n\n.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {\n color: #856404;\n background-color: #ffe8a1;\n}\n\n.list-group-item-warning.list-group-item-action.active {\n color: #fff;\n background-color: #856404;\n border-color: #856404;\n}\n\n.list-group-item-danger {\n color: #721c24;\n background-color: #f5c6cb;\n}\n\n.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {\n color: #721c24;\n background-color: #f1b0b7;\n}\n\n.list-group-item-danger.list-group-item-action.active {\n color: #fff;\n background-color: #721c24;\n border-color: #721c24;\n}\n\n.list-group-item-light {\n color: #818182;\n background-color: #fdfdfe;\n}\n\n.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {\n color: #818182;\n background-color: #ececf6;\n}\n\n.list-group-item-light.list-group-item-action.active {\n color: #fff;\n background-color: #818182;\n border-color: #818182;\n}\n\n.list-group-item-dark {\n color: #1b1e21;\n background-color: #c6c8ca;\n}\n\n.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {\n color: #1b1e21;\n background-color: #b9bbbe;\n}\n\n.list-group-item-dark.list-group-item-action.active {\n color: #fff;\n background-color: #1b1e21;\n border-color: #1b1e21;\n}\n\n.close {\n float: right;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1;\n color: #000;\n text-shadow: 0 1px 0 #fff;\n opacity: .5;\n}\n\n.close:hover {\n color: #000;\n text-decoration: none;\n}\n\n.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {\n opacity: .75;\n}\n\nbutton.close {\n padding: 0;\n background-color: transparent;\n border: 0;\n appearance: none;\n}\n\na.close.disabled {\n pointer-events: none;\n}\n\n.toast {\n max-width: 350px;\n overflow: hidden;\n font-size: 0.875rem;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.1);\n box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);\n backdrop-filter: blur(10px);\n opacity: 0;\n border-radius: 0.25rem;\n}\n\n.toast:not(:last-child) {\n margin-bottom: 0.75rem;\n}\n\n.toast.showing {\n opacity: 1;\n}\n\n.toast.show {\n display: block;\n opacity: 1;\n}\n\n.toast.hide {\n display: none;\n}\n\n.toast-header {\n display: flex;\n align-items: center;\n padding: 0.25rem 0.75rem;\n color: #6c757d;\n background-color: rgba(255, 255, 255, 0.85);\n background-clip: padding-box;\n border-bottom: 1px solid rgba(0, 0, 0, 0.05);\n}\n\n.toast-body {\n padding: 0.75rem;\n}\n\n.modal-open {\n overflow: hidden;\n}\n\n.modal-open .modal {\n overflow-x: hidden;\n overflow-y: auto;\n}\n\n.modal {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1050;\n display: none;\n width: 100%;\n height: 100%;\n overflow: hidden;\n outline: 0;\n}\n\n.modal-dialog {\n position: relative;\n width: auto;\n margin: 0.5rem;\n pointer-events: none;\n}\n\n.modal.fade .modal-dialog {\n transition: transform 0.3s ease-out;\n transform: translate(0, -50px);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .modal.fade .modal-dialog {\n transition: none;\n }\n}\n\n.modal.show .modal-dialog {\n transform: none;\n}\n\n.modal-dialog-scrollable {\n display: flex;\n max-height: calc(100% - 1rem);\n}\n\n.modal-dialog-scrollable .modal-content {\n max-height: calc(100vh - 1rem);\n overflow: hidden;\n}\n\n.modal-dialog-scrollable .modal-header,\n.modal-dialog-scrollable .modal-footer {\n flex-shrink: 0;\n}\n\n.modal-dialog-scrollable .modal-body {\n overflow-y: auto;\n}\n\n.modal-dialog-centered {\n display: flex;\n align-items: center;\n min-height: calc(100% - 1rem);\n}\n\n.modal-dialog-centered::before {\n display: block;\n height: calc(100vh - 1rem);\n content: \"\";\n}\n\n.modal-dialog-centered.modal-dialog-scrollable {\n flex-direction: column;\n justify-content: center;\n height: 100%;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable .modal-content {\n max-height: none;\n}\n\n.modal-dialog-centered.modal-dialog-scrollable::before {\n content: none;\n}\n\n.modal-content {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n pointer-events: auto;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n outline: 0;\n}\n\n.modal-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 1040;\n width: 100vw;\n height: 100vh;\n background-color: #000;\n}\n\n.modal-backdrop.fade {\n opacity: 0;\n}\n\n.modal-backdrop.show {\n opacity: 0.5;\n}\n\n.modal-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 1rem 1rem;\n border-bottom: 1px solid #dee2e6;\n border-top-left-radius: 0.3rem;\n border-top-right-radius: 0.3rem;\n}\n\n.modal-header .close {\n padding: 1rem 1rem;\n margin: -1rem -1rem -1rem auto;\n}\n\n.modal-title {\n margin-bottom: 0;\n line-height: 1.5;\n}\n\n.modal-body {\n position: relative;\n flex: 1 1 auto;\n padding: 1rem;\n}\n\n.modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n padding: 1rem;\n border-top: 1px solid #dee2e6;\n border-bottom-right-radius: 0.3rem;\n border-bottom-left-radius: 0.3rem;\n}\n\n.modal-footer > :not(:first-child) {\n margin-left: .25rem;\n}\n\n.modal-footer > :not(:last-child) {\n margin-right: .25rem;\n}\n\n.modal-scrollbar-measure {\n position: absolute;\n top: -9999px;\n width: 50px;\n height: 50px;\n overflow: scroll;\n}\n\n@media (min-width: 576px) {\n .modal-dialog {\n max-width: 500px;\n margin: 1.75rem auto;\n }\n .modal-dialog-scrollable {\n max-height: calc(100% - 3.5rem);\n }\n .modal-dialog-scrollable .modal-content {\n max-height: calc(100vh - 3.5rem);\n }\n .modal-dialog-centered {\n min-height: calc(100% - 3.5rem);\n }\n .modal-dialog-centered::before {\n height: calc(100vh - 3.5rem);\n }\n .modal-sm {\n max-width: 300px;\n }\n}\n\n@media (min-width: 992px) {\n .modal-lg,\n .modal-xl {\n max-width: 800px;\n }\n}\n\n@media (min-width: 1200px) {\n .modal-xl {\n max-width: 1140px;\n }\n}\n\n.tooltip {\n position: absolute;\n z-index: 1070;\n display: block;\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n opacity: 0;\n}\n\n.tooltip.show {\n opacity: 0.9;\n}\n\n.tooltip .arrow {\n position: absolute;\n display: block;\n width: 0.8rem;\n height: 0.4rem;\n}\n\n.tooltip .arrow::before {\n position: absolute;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-tooltip-top, .bs-tooltip-auto[x-placement^=\"top\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^=\"top\"] .arrow {\n bottom: 0;\n}\n\n.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^=\"top\"] .arrow::before {\n top: 0;\n border-width: 0.4rem 0.4rem 0;\n border-top-color: #000;\n}\n\n.bs-tooltip-right, .bs-tooltip-auto[x-placement^=\"right\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^=\"right\"] .arrow {\n left: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^=\"right\"] .arrow::before {\n right: 0;\n border-width: 0.4rem 0.4rem 0.4rem 0;\n border-right-color: #000;\n}\n\n.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^=\"bottom\"] {\n padding: 0.4rem 0;\n}\n\n.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow {\n top: 0;\n}\n\n.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^=\"bottom\"] .arrow::before {\n bottom: 0;\n border-width: 0 0.4rem 0.4rem;\n border-bottom-color: #000;\n}\n\n.bs-tooltip-left, .bs-tooltip-auto[x-placement^=\"left\"] {\n padding: 0 0.4rem;\n}\n\n.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^=\"left\"] .arrow {\n right: 0;\n width: 0.4rem;\n height: 0.8rem;\n}\n\n.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^=\"left\"] .arrow::before {\n left: 0;\n border-width: 0.4rem 0 0.4rem 0.4rem;\n border-left-color: #000;\n}\n\n.tooltip-inner {\n max-width: 200px;\n padding: 0.25rem 0.5rem;\n color: #fff;\n text-align: center;\n background-color: #000;\n border-radius: 0.25rem;\n}\n\n.popover {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 1060;\n display: block;\n max-width: 276px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-style: normal;\n font-weight: 400;\n line-height: 1.5;\n text-align: left;\n text-align: start;\n text-decoration: none;\n text-shadow: none;\n text-transform: none;\n letter-spacing: normal;\n word-break: normal;\n word-spacing: normal;\n white-space: normal;\n line-break: auto;\n font-size: 0.875rem;\n word-wrap: break-word;\n background-color: #fff;\n background-clip: padding-box;\n border: 1px solid rgba(0, 0, 0, 0.2);\n border-radius: 0.3rem;\n}\n\n.popover .arrow {\n position: absolute;\n display: block;\n width: 1rem;\n height: 0.5rem;\n margin: 0 0.3rem;\n}\n\n.popover .arrow::before, .popover .arrow::after {\n position: absolute;\n display: block;\n content: \"\";\n border-color: transparent;\n border-style: solid;\n}\n\n.bs-popover-top, .bs-popover-auto[x-placement^=\"top\"] {\n margin-bottom: 0.5rem;\n}\n\n.bs-popover-top > .arrow, .bs-popover-auto[x-placement^=\"top\"] > .arrow {\n bottom: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^=\"top\"] > .arrow::before {\n bottom: 0;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^=\"top\"] > .arrow::after {\n bottom: 1px;\n border-width: 0.5rem 0.5rem 0;\n border-top-color: #fff;\n}\n\n.bs-popover-right, .bs-popover-auto[x-placement^=\"right\"] {\n margin-left: 0.5rem;\n}\n\n.bs-popover-right > .arrow, .bs-popover-auto[x-placement^=\"right\"] > .arrow {\n left: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^=\"right\"] > .arrow::before {\n left: 0;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^=\"right\"] > .arrow::after {\n left: 1px;\n border-width: 0.5rem 0.5rem 0.5rem 0;\n border-right-color: #fff;\n}\n\n.bs-popover-bottom, .bs-popover-auto[x-placement^=\"bottom\"] {\n margin-top: 0.5rem;\n}\n\n.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow {\n top: calc((0.5rem + 1px) * -1);\n}\n\n.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::before {\n top: 0;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^=\"bottom\"] > .arrow::after {\n top: 1px;\n border-width: 0 0.5rem 0.5rem 0.5rem;\n border-bottom-color: #fff;\n}\n\n.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^=\"bottom\"] .popover-header::before {\n position: absolute;\n top: 0;\n left: 50%;\n display: block;\n width: 1rem;\n margin-left: -0.5rem;\n content: \"\";\n border-bottom: 1px solid #f7f7f7;\n}\n\n.bs-popover-left, .bs-popover-auto[x-placement^=\"left\"] {\n margin-right: 0.5rem;\n}\n\n.bs-popover-left > .arrow, .bs-popover-auto[x-placement^=\"left\"] > .arrow {\n right: calc((0.5rem + 1px) * -1);\n width: 0.5rem;\n height: 1rem;\n margin: 0.3rem 0;\n}\n\n.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^=\"left\"] > .arrow::before {\n right: 0;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: rgba(0, 0, 0, 0.25);\n}\n\n.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^=\"left\"] > .arrow::after {\n right: 1px;\n border-width: 0.5rem 0 0.5rem 0.5rem;\n border-left-color: #fff;\n}\n\n.popover-header {\n padding: 0.5rem 0.75rem;\n margin-bottom: 0;\n font-size: 1rem;\n background-color: #f7f7f7;\n border-bottom: 1px solid #ebebeb;\n border-top-left-radius: calc(0.3rem - 1px);\n border-top-right-radius: calc(0.3rem - 1px);\n}\n\n.popover-header:empty {\n display: none;\n}\n\n.popover-body {\n padding: 0.5rem 0.75rem;\n color: #212529;\n}\n\n.carousel {\n position: relative;\n}\n\n.carousel.pointer-event {\n touch-action: pan-y;\n}\n\n.carousel-inner {\n position: relative;\n width: 100%;\n overflow: hidden;\n}\n\n.carousel-inner::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.carousel-item {\n position: relative;\n display: none;\n float: left;\n width: 100%;\n margin-right: -100%;\n backface-visibility: hidden;\n transition: transform 0.6s ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-item {\n transition: none;\n }\n}\n\n.carousel-item.active,\n.carousel-item-next,\n.carousel-item-prev {\n display: block;\n}\n\n.carousel-item-next:not(.carousel-item-left),\n.active.carousel-item-right {\n transform: translateX(100%);\n}\n\n.carousel-item-prev:not(.carousel-item-right),\n.active.carousel-item-left {\n transform: translateX(-100%);\n}\n\n.carousel-fade .carousel-item {\n opacity: 0;\n transition-property: opacity;\n transform: none;\n}\n\n.carousel-fade .carousel-item.active,\n.carousel-fade .carousel-item-next.carousel-item-left,\n.carousel-fade .carousel-item-prev.carousel-item-right {\n z-index: 1;\n opacity: 1;\n}\n\n.carousel-fade .active.carousel-item-left,\n.carousel-fade .active.carousel-item-right {\n z-index: 0;\n opacity: 0;\n transition: 0s 0.6s opacity;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-fade .active.carousel-item-left,\n .carousel-fade .active.carousel-item-right {\n transition: none;\n }\n}\n\n.carousel-control-prev,\n.carousel-control-next {\n position: absolute;\n top: 0;\n bottom: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 15%;\n color: #fff;\n text-align: center;\n opacity: 0.5;\n transition: opacity 0.15s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-control-prev,\n .carousel-control-next {\n transition: none;\n }\n}\n\n.carousel-control-prev:hover, .carousel-control-prev:focus,\n.carousel-control-next:hover,\n.carousel-control-next:focus {\n color: #fff;\n text-decoration: none;\n outline: 0;\n opacity: 0.9;\n}\n\n.carousel-control-prev {\n left: 0;\n}\n\n.carousel-control-next {\n right: 0;\n}\n\n.carousel-control-prev-icon,\n.carousel-control-next-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n background: no-repeat 50% / 100% 100%;\n}\n\n.carousel-control-prev-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3e%3c/svg%3e\");\n}\n\n.carousel-control-next-icon {\n background-image: url(\"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3e%3c/svg%3e\");\n}\n\n.carousel-indicators {\n position: absolute;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 15;\n display: flex;\n justify-content: center;\n padding-left: 0;\n margin-right: 15%;\n margin-left: 15%;\n list-style: none;\n}\n\n.carousel-indicators li {\n box-sizing: content-box;\n flex: 0 1 auto;\n width: 30px;\n height: 3px;\n margin-right: 3px;\n margin-left: 3px;\n text-indent: -999px;\n cursor: pointer;\n background-color: #fff;\n background-clip: padding-box;\n border-top: 10px solid transparent;\n border-bottom: 10px solid transparent;\n opacity: .5;\n transition: opacity 0.6s ease;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .carousel-indicators li {\n transition: none;\n }\n}\n\n.carousel-indicators .active {\n opacity: 1;\n}\n\n.carousel-caption {\n position: absolute;\n right: 15%;\n bottom: 20px;\n left: 15%;\n z-index: 10;\n padding-top: 20px;\n padding-bottom: 20px;\n color: #fff;\n text-align: center;\n}\n\n@keyframes spinner-border {\n to {\n transform: rotate(360deg);\n }\n}\n\n.spinner-border {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: text-bottom;\n border: 0.25em solid currentColor;\n border-right-color: transparent;\n border-radius: 50%;\n animation: spinner-border .75s linear infinite;\n}\n\n.spinner-border-sm {\n width: 1rem;\n height: 1rem;\n border-width: 0.2em;\n}\n\n@keyframes spinner-grow {\n 0% {\n transform: scale(0);\n }\n 50% {\n opacity: 1;\n }\n}\n\n.spinner-grow {\n display: inline-block;\n width: 2rem;\n height: 2rem;\n vertical-align: text-bottom;\n background-color: currentColor;\n border-radius: 50%;\n opacity: 0;\n animation: spinner-grow .75s linear infinite;\n}\n\n.spinner-grow-sm {\n width: 1rem;\n height: 1rem;\n}\n\n.align-baseline {\n vertical-align: baseline !important;\n}\n\n.align-top {\n vertical-align: top !important;\n}\n\n.align-middle {\n vertical-align: middle !important;\n}\n\n.align-bottom {\n vertical-align: bottom !important;\n}\n\n.align-text-bottom {\n vertical-align: text-bottom !important;\n}\n\n.align-text-top {\n vertical-align: text-top !important;\n}\n\n.bg-primary {\n background-color: #007bff !important;\n}\n\na.bg-primary:hover, a.bg-primary:focus,\nbutton.bg-primary:hover,\nbutton.bg-primary:focus {\n background-color: #0062cc !important;\n}\n\n.bg-secondary {\n background-color: #6c757d !important;\n}\n\na.bg-secondary:hover, a.bg-secondary:focus,\nbutton.bg-secondary:hover,\nbutton.bg-secondary:focus {\n background-color: #545b62 !important;\n}\n\n.bg-success {\n background-color: #28a745 !important;\n}\n\na.bg-success:hover, a.bg-success:focus,\nbutton.bg-success:hover,\nbutton.bg-success:focus {\n background-color: #1e7e34 !important;\n}\n\n.bg-info {\n background-color: #17a2b8 !important;\n}\n\na.bg-info:hover, a.bg-info:focus,\nbutton.bg-info:hover,\nbutton.bg-info:focus {\n background-color: #117a8b !important;\n}\n\n.bg-warning {\n background-color: #ffc107 !important;\n}\n\na.bg-warning:hover, a.bg-warning:focus,\nbutton.bg-warning:hover,\nbutton.bg-warning:focus {\n background-color: #d39e00 !important;\n}\n\n.bg-danger {\n background-color: #dc3545 !important;\n}\n\na.bg-danger:hover, a.bg-danger:focus,\nbutton.bg-danger:hover,\nbutton.bg-danger:focus {\n background-color: #bd2130 !important;\n}\n\n.bg-light {\n background-color: #f8f9fa !important;\n}\n\na.bg-light:hover, a.bg-light:focus,\nbutton.bg-light:hover,\nbutton.bg-light:focus {\n background-color: #dae0e5 !important;\n}\n\n.bg-dark {\n background-color: #343a40 !important;\n}\n\na.bg-dark:hover, a.bg-dark:focus,\nbutton.bg-dark:hover,\nbutton.bg-dark:focus {\n background-color: #1d2124 !important;\n}\n\n.bg-white {\n background-color: #fff !important;\n}\n\n.bg-transparent {\n background-color: transparent !important;\n}\n\n.border {\n border: 1px solid #dee2e6 !important;\n}\n\n.border-top {\n border-top: 1px solid #dee2e6 !important;\n}\n\n.border-right {\n border-right: 1px solid #dee2e6 !important;\n}\n\n.border-bottom {\n border-bottom: 1px solid #dee2e6 !important;\n}\n\n.border-left {\n border-left: 1px solid #dee2e6 !important;\n}\n\n.border-0 {\n border: 0 !important;\n}\n\n.border-top-0 {\n border-top: 0 !important;\n}\n\n.border-right-0 {\n border-right: 0 !important;\n}\n\n.border-bottom-0 {\n border-bottom: 0 !important;\n}\n\n.border-left-0 {\n border-left: 0 !important;\n}\n\n.border-primary {\n border-color: #007bff !important;\n}\n\n.border-secondary {\n border-color: #6c757d !important;\n}\n\n.border-success {\n border-color: #28a745 !important;\n}\n\n.border-info {\n border-color: #17a2b8 !important;\n}\n\n.border-warning {\n border-color: #ffc107 !important;\n}\n\n.border-danger {\n border-color: #dc3545 !important;\n}\n\n.border-light {\n border-color: #f8f9fa !important;\n}\n\n.border-dark {\n border-color: #343a40 !important;\n}\n\n.border-white {\n border-color: #fff !important;\n}\n\n.rounded-sm {\n border-radius: 0.2rem !important;\n}\n\n.rounded {\n border-radius: 0.25rem !important;\n}\n\n.rounded-top {\n border-top-left-radius: 0.25rem !important;\n border-top-right-radius: 0.25rem !important;\n}\n\n.rounded-right {\n border-top-right-radius: 0.25rem !important;\n border-bottom-right-radius: 0.25rem !important;\n}\n\n.rounded-bottom {\n border-bottom-right-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-left {\n border-top-left-radius: 0.25rem !important;\n border-bottom-left-radius: 0.25rem !important;\n}\n\n.rounded-lg {\n border-radius: 0.3rem !important;\n}\n\n.rounded-circle {\n border-radius: 50% !important;\n}\n\n.rounded-pill {\n border-radius: 50rem !important;\n}\n\n.rounded-0 {\n border-radius: 0 !important;\n}\n\n.clearfix::after {\n display: block;\n clear: both;\n content: \"\";\n}\n\n.d-none {\n display: none !important;\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-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@media (min-width: 576px) {\n .d-sm-none {\n display: none !important;\n }\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-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}\n\n@media (min-width: 768px) {\n .d-md-none {\n display: none !important;\n }\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-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}\n\n@media (min-width: 992px) {\n .d-lg-none {\n display: none !important;\n }\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-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}\n\n@media (min-width: 1200px) {\n .d-xl-none {\n display: none !important;\n }\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-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}\n\n@media print {\n .d-print-none {\n display: none !important;\n }\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-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}\n\n.embed-responsive {\n position: relative;\n display: block;\n width: 100%;\n padding: 0;\n overflow: hidden;\n}\n\n.embed-responsive::before {\n display: block;\n content: \"\";\n}\n\n.embed-responsive .embed-responsive-item,\n.embed-responsive iframe,\n.embed-responsive embed,\n.embed-responsive object,\n.embed-responsive video {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 0;\n}\n\n.embed-responsive-21by9::before {\n padding-top: 42.857143%;\n}\n\n.embed-responsive-16by9::before {\n padding-top: 56.25%;\n}\n\n.embed-responsive-4by3::before {\n padding-top: 75%;\n}\n\n.embed-responsive-1by1::before {\n padding-top: 100%;\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-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.flex-fill {\n flex: 1 1 auto !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.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.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@media (min-width: 576px) {\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-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 .flex-sm-fill {\n flex: 1 1 auto !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 .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 .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}\n\n@media (min-width: 768px) {\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-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 .flex-md-fill {\n flex: 1 1 auto !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 .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 .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}\n\n@media (min-width: 992px) {\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-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 .flex-lg-fill {\n flex: 1 1 auto !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 .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 .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}\n\n@media (min-width: 1200px) {\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-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 .flex-xl-fill {\n flex: 1 1 auto !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 .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 .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}\n\n.float-left {\n float: left !important;\n}\n\n.float-right {\n float: right !important;\n}\n\n.float-none {\n float: none !important;\n}\n\n@media (min-width: 576px) {\n .float-sm-left {\n float: left !important;\n }\n .float-sm-right {\n float: right !important;\n }\n .float-sm-none {\n float: none !important;\n }\n}\n\n@media (min-width: 768px) {\n .float-md-left {\n float: left !important;\n }\n .float-md-right {\n float: right !important;\n }\n .float-md-none {\n float: none !important;\n }\n}\n\n@media (min-width: 992px) {\n .float-lg-left {\n float: left !important;\n }\n .float-lg-right {\n float: right !important;\n }\n .float-lg-none {\n float: none !important;\n }\n}\n\n@media (min-width: 1200px) {\n .float-xl-left {\n float: left !important;\n }\n .float-xl-right {\n float: right !important;\n }\n .float-xl-none {\n float: none !important;\n }\n}\n\n.overflow-auto {\n overflow: auto !important;\n}\n\n.overflow-hidden {\n overflow: hidden !important;\n}\n\n.position-static {\n position: static !important;\n}\n\n.position-relative {\n position: relative !important;\n}\n\n.position-absolute {\n position: absolute !important;\n}\n\n.position-fixed {\n position: fixed !important;\n}\n\n.position-sticky {\n position: sticky !important;\n}\n\n.fixed-top {\n position: fixed;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1030;\n}\n\n.fixed-bottom {\n position: fixed;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1030;\n}\n\n@supports (position: sticky) {\n .sticky-top {\n position: sticky;\n top: 0;\n z-index: 1020;\n }\n}\n\n.sr-only {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n\n.sr-only-focusable:active, .sr-only-focusable:focus {\n position: static;\n width: auto;\n height: auto;\n overflow: visible;\n clip: auto;\n white-space: normal;\n}\n\n.shadow-sm {\n box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;\n}\n\n.shadow {\n box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;\n}\n\n.shadow-lg {\n box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;\n}\n\n.shadow-none {\n box-shadow: none !important;\n}\n\n.w-25 {\n width: 25% !important;\n}\n\n.w-50 {\n width: 50% !important;\n}\n\n.w-75 {\n width: 75% !important;\n}\n\n.w-100 {\n width: 100% !important;\n}\n\n.w-auto {\n width: auto !important;\n}\n\n.h-25 {\n height: 25% !important;\n}\n\n.h-50 {\n height: 50% !important;\n}\n\n.h-75 {\n height: 75% !important;\n}\n\n.h-100 {\n height: 100% !important;\n}\n\n.h-auto {\n height: auto !important;\n}\n\n.mw-100 {\n max-width: 100% !important;\n}\n\n.mh-100 {\n max-height: 100% !important;\n}\n\n.min-vw-100 {\n min-width: 100vw !important;\n}\n\n.min-vh-100 {\n min-height: 100vh !important;\n}\n\n.vw-100 {\n width: 100vw !important;\n}\n\n.vh-100 {\n height: 100vh !important;\n}\n\n.stretched-link::after {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 1;\n pointer-events: auto;\n content: \"\";\n background-color: rgba(0, 0, 0, 0);\n}\n\n.m-0 {\n margin: 0 !important;\n}\n\n.mt-0,\n.my-0 {\n margin-top: 0 !important;\n}\n\n.mr-0,\n.mx-0 {\n margin-right: 0 !important;\n}\n\n.mb-0,\n.my-0 {\n margin-bottom: 0 !important;\n}\n\n.ml-0,\n.mx-0 {\n margin-left: 0 !important;\n}\n\n.m-1 {\n margin: 0.25rem !important;\n}\n\n.mt-1,\n.my-1 {\n margin-top: 0.25rem !important;\n}\n\n.mr-1,\n.mx-1 {\n margin-right: 0.25rem !important;\n}\n\n.mb-1,\n.my-1 {\n margin-bottom: 0.25rem !important;\n}\n\n.ml-1,\n.mx-1 {\n margin-left: 0.25rem !important;\n}\n\n.m-2 {\n margin: 0.5rem !important;\n}\n\n.mt-2,\n.my-2 {\n margin-top: 0.5rem !important;\n}\n\n.mr-2,\n.mx-2 {\n margin-right: 0.5rem !important;\n}\n\n.mb-2,\n.my-2 {\n margin-bottom: 0.5rem !important;\n}\n\n.ml-2,\n.mx-2 {\n margin-left: 0.5rem !important;\n}\n\n.m-3 {\n margin: 1rem !important;\n}\n\n.mt-3,\n.my-3 {\n margin-top: 1rem !important;\n}\n\n.mr-3,\n.mx-3 {\n margin-right: 1rem !important;\n}\n\n.mb-3,\n.my-3 {\n margin-bottom: 1rem !important;\n}\n\n.ml-3,\n.mx-3 {\n margin-left: 1rem !important;\n}\n\n.m-4 {\n margin: 1.5rem !important;\n}\n\n.mt-4,\n.my-4 {\n margin-top: 1.5rem !important;\n}\n\n.mr-4,\n.mx-4 {\n margin-right: 1.5rem !important;\n}\n\n.mb-4,\n.my-4 {\n margin-bottom: 1.5rem !important;\n}\n\n.ml-4,\n.mx-4 {\n margin-left: 1.5rem !important;\n}\n\n.m-5 {\n margin: 3rem !important;\n}\n\n.mt-5,\n.my-5 {\n margin-top: 3rem !important;\n}\n\n.mr-5,\n.mx-5 {\n margin-right: 3rem !important;\n}\n\n.mb-5,\n.my-5 {\n margin-bottom: 3rem !important;\n}\n\n.ml-5,\n.mx-5 {\n margin-left: 3rem !important;\n}\n\n.p-0 {\n padding: 0 !important;\n}\n\n.pt-0,\n.py-0 {\n padding-top: 0 !important;\n}\n\n.pr-0,\n.px-0 {\n padding-right: 0 !important;\n}\n\n.pb-0,\n.py-0 {\n padding-bottom: 0 !important;\n}\n\n.pl-0,\n.px-0 {\n padding-left: 0 !important;\n}\n\n.p-1 {\n padding: 0.25rem !important;\n}\n\n.pt-1,\n.py-1 {\n padding-top: 0.25rem !important;\n}\n\n.pr-1,\n.px-1 {\n padding-right: 0.25rem !important;\n}\n\n.pb-1,\n.py-1 {\n padding-bottom: 0.25rem !important;\n}\n\n.pl-1,\n.px-1 {\n padding-left: 0.25rem !important;\n}\n\n.p-2 {\n padding: 0.5rem !important;\n}\n\n.pt-2,\n.py-2 {\n padding-top: 0.5rem !important;\n}\n\n.pr-2,\n.px-2 {\n padding-right: 0.5rem !important;\n}\n\n.pb-2,\n.py-2 {\n padding-bottom: 0.5rem !important;\n}\n\n.pl-2,\n.px-2 {\n padding-left: 0.5rem !important;\n}\n\n.p-3 {\n padding: 1rem !important;\n}\n\n.pt-3,\n.py-3 {\n padding-top: 1rem !important;\n}\n\n.pr-3,\n.px-3 {\n padding-right: 1rem !important;\n}\n\n.pb-3,\n.py-3 {\n padding-bottom: 1rem !important;\n}\n\n.pl-3,\n.px-3 {\n padding-left: 1rem !important;\n}\n\n.p-4 {\n padding: 1.5rem !important;\n}\n\n.pt-4,\n.py-4 {\n padding-top: 1.5rem !important;\n}\n\n.pr-4,\n.px-4 {\n padding-right: 1.5rem !important;\n}\n\n.pb-4,\n.py-4 {\n padding-bottom: 1.5rem !important;\n}\n\n.pl-4,\n.px-4 {\n padding-left: 1.5rem !important;\n}\n\n.p-5 {\n padding: 3rem !important;\n}\n\n.pt-5,\n.py-5 {\n padding-top: 3rem !important;\n}\n\n.pr-5,\n.px-5 {\n padding-right: 3rem !important;\n}\n\n.pb-5,\n.py-5 {\n padding-bottom: 3rem !important;\n}\n\n.pl-5,\n.px-5 {\n padding-left: 3rem !important;\n}\n\n.m-n1 {\n margin: -0.25rem !important;\n}\n\n.mt-n1,\n.my-n1 {\n margin-top: -0.25rem !important;\n}\n\n.mr-n1,\n.mx-n1 {\n margin-right: -0.25rem !important;\n}\n\n.mb-n1,\n.my-n1 {\n margin-bottom: -0.25rem !important;\n}\n\n.ml-n1,\n.mx-n1 {\n margin-left: -0.25rem !important;\n}\n\n.m-n2 {\n margin: -0.5rem !important;\n}\n\n.mt-n2,\n.my-n2 {\n margin-top: -0.5rem !important;\n}\n\n.mr-n2,\n.mx-n2 {\n margin-right: -0.5rem !important;\n}\n\n.mb-n2,\n.my-n2 {\n margin-bottom: -0.5rem !important;\n}\n\n.ml-n2,\n.mx-n2 {\n margin-left: -0.5rem !important;\n}\n\n.m-n3 {\n margin: -1rem !important;\n}\n\n.mt-n3,\n.my-n3 {\n margin-top: -1rem !important;\n}\n\n.mr-n3,\n.mx-n3 {\n margin-right: -1rem !important;\n}\n\n.mb-n3,\n.my-n3 {\n margin-bottom: -1rem !important;\n}\n\n.ml-n3,\n.mx-n3 {\n margin-left: -1rem !important;\n}\n\n.m-n4 {\n margin: -1.5rem !important;\n}\n\n.mt-n4,\n.my-n4 {\n margin-top: -1.5rem !important;\n}\n\n.mr-n4,\n.mx-n4 {\n margin-right: -1.5rem !important;\n}\n\n.mb-n4,\n.my-n4 {\n margin-bottom: -1.5rem !important;\n}\n\n.ml-n4,\n.mx-n4 {\n margin-left: -1.5rem !important;\n}\n\n.m-n5 {\n margin: -3rem !important;\n}\n\n.mt-n5,\n.my-n5 {\n margin-top: -3rem !important;\n}\n\n.mr-n5,\n.mx-n5 {\n margin-right: -3rem !important;\n}\n\n.mb-n5,\n.my-n5 {\n margin-bottom: -3rem !important;\n}\n\n.ml-n5,\n.mx-n5 {\n margin-left: -3rem !important;\n}\n\n.m-auto {\n margin: auto !important;\n}\n\n.mt-auto,\n.my-auto {\n margin-top: auto !important;\n}\n\n.mr-auto,\n.mx-auto {\n margin-right: auto !important;\n}\n\n.mb-auto,\n.my-auto {\n margin-bottom: auto !important;\n}\n\n.ml-auto,\n.mx-auto {\n margin-left: auto !important;\n}\n\n@media (min-width: 576px) {\n .m-sm-0 {\n margin: 0 !important;\n }\n .mt-sm-0,\n .my-sm-0 {\n margin-top: 0 !important;\n }\n .mr-sm-0,\n .mx-sm-0 {\n margin-right: 0 !important;\n }\n .mb-sm-0,\n .my-sm-0 {\n margin-bottom: 0 !important;\n }\n .ml-sm-0,\n .mx-sm-0 {\n margin-left: 0 !important;\n }\n .m-sm-1 {\n margin: 0.25rem !important;\n }\n .mt-sm-1,\n .my-sm-1 {\n margin-top: 0.25rem !important;\n }\n .mr-sm-1,\n .mx-sm-1 {\n margin-right: 0.25rem !important;\n }\n .mb-sm-1,\n .my-sm-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-sm-1,\n .mx-sm-1 {\n margin-left: 0.25rem !important;\n }\n .m-sm-2 {\n margin: 0.5rem !important;\n }\n .mt-sm-2,\n .my-sm-2 {\n margin-top: 0.5rem !important;\n }\n .mr-sm-2,\n .mx-sm-2 {\n margin-right: 0.5rem !important;\n }\n .mb-sm-2,\n .my-sm-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-sm-2,\n .mx-sm-2 {\n margin-left: 0.5rem !important;\n }\n .m-sm-3 {\n margin: 1rem !important;\n }\n .mt-sm-3,\n .my-sm-3 {\n margin-top: 1rem !important;\n }\n .mr-sm-3,\n .mx-sm-3 {\n margin-right: 1rem !important;\n }\n .mb-sm-3,\n .my-sm-3 {\n margin-bottom: 1rem !important;\n }\n .ml-sm-3,\n .mx-sm-3 {\n margin-left: 1rem !important;\n }\n .m-sm-4 {\n margin: 1.5rem !important;\n }\n .mt-sm-4,\n .my-sm-4 {\n margin-top: 1.5rem !important;\n }\n .mr-sm-4,\n .mx-sm-4 {\n margin-right: 1.5rem !important;\n }\n .mb-sm-4,\n .my-sm-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-sm-4,\n .mx-sm-4 {\n margin-left: 1.5rem !important;\n }\n .m-sm-5 {\n margin: 3rem !important;\n }\n .mt-sm-5,\n .my-sm-5 {\n margin-top: 3rem !important;\n }\n .mr-sm-5,\n .mx-sm-5 {\n margin-right: 3rem !important;\n }\n .mb-sm-5,\n .my-sm-5 {\n margin-bottom: 3rem !important;\n }\n .ml-sm-5,\n .mx-sm-5 {\n margin-left: 3rem !important;\n }\n .p-sm-0 {\n padding: 0 !important;\n }\n .pt-sm-0,\n .py-sm-0 {\n padding-top: 0 !important;\n }\n .pr-sm-0,\n .px-sm-0 {\n padding-right: 0 !important;\n }\n .pb-sm-0,\n .py-sm-0 {\n padding-bottom: 0 !important;\n }\n .pl-sm-0,\n .px-sm-0 {\n padding-left: 0 !important;\n }\n .p-sm-1 {\n padding: 0.25rem !important;\n }\n .pt-sm-1,\n .py-sm-1 {\n padding-top: 0.25rem !important;\n }\n .pr-sm-1,\n .px-sm-1 {\n padding-right: 0.25rem !important;\n }\n .pb-sm-1,\n .py-sm-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-sm-1,\n .px-sm-1 {\n padding-left: 0.25rem !important;\n }\n .p-sm-2 {\n padding: 0.5rem !important;\n }\n .pt-sm-2,\n .py-sm-2 {\n padding-top: 0.5rem !important;\n }\n .pr-sm-2,\n .px-sm-2 {\n padding-right: 0.5rem !important;\n }\n .pb-sm-2,\n .py-sm-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-sm-2,\n .px-sm-2 {\n padding-left: 0.5rem !important;\n }\n .p-sm-3 {\n padding: 1rem !important;\n }\n .pt-sm-3,\n .py-sm-3 {\n padding-top: 1rem !important;\n }\n .pr-sm-3,\n .px-sm-3 {\n padding-right: 1rem !important;\n }\n .pb-sm-3,\n .py-sm-3 {\n padding-bottom: 1rem !important;\n }\n .pl-sm-3,\n .px-sm-3 {\n padding-left: 1rem !important;\n }\n .p-sm-4 {\n padding: 1.5rem !important;\n }\n .pt-sm-4,\n .py-sm-4 {\n padding-top: 1.5rem !important;\n }\n .pr-sm-4,\n .px-sm-4 {\n padding-right: 1.5rem !important;\n }\n .pb-sm-4,\n .py-sm-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-sm-4,\n .px-sm-4 {\n padding-left: 1.5rem !important;\n }\n .p-sm-5 {\n padding: 3rem !important;\n }\n .pt-sm-5,\n .py-sm-5 {\n padding-top: 3rem !important;\n }\n .pr-sm-5,\n .px-sm-5 {\n padding-right: 3rem !important;\n }\n .pb-sm-5,\n .py-sm-5 {\n padding-bottom: 3rem !important;\n }\n .pl-sm-5,\n .px-sm-5 {\n padding-left: 3rem !important;\n }\n .m-sm-n1 {\n margin: -0.25rem !important;\n }\n .mt-sm-n1,\n .my-sm-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-sm-n1,\n .mx-sm-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-sm-n1,\n .my-sm-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-sm-n1,\n .mx-sm-n1 {\n margin-left: -0.25rem !important;\n }\n .m-sm-n2 {\n margin: -0.5rem !important;\n }\n .mt-sm-n2,\n .my-sm-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-sm-n2,\n .mx-sm-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-sm-n2,\n .my-sm-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-sm-n2,\n .mx-sm-n2 {\n margin-left: -0.5rem !important;\n }\n .m-sm-n3 {\n margin: -1rem !important;\n }\n .mt-sm-n3,\n .my-sm-n3 {\n margin-top: -1rem !important;\n }\n .mr-sm-n3,\n .mx-sm-n3 {\n margin-right: -1rem !important;\n }\n .mb-sm-n3,\n .my-sm-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-sm-n3,\n .mx-sm-n3 {\n margin-left: -1rem !important;\n }\n .m-sm-n4 {\n margin: -1.5rem !important;\n }\n .mt-sm-n4,\n .my-sm-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-sm-n4,\n .mx-sm-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-sm-n4,\n .my-sm-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-sm-n4,\n .mx-sm-n4 {\n margin-left: -1.5rem !important;\n }\n .m-sm-n5 {\n margin: -3rem !important;\n }\n .mt-sm-n5,\n .my-sm-n5 {\n margin-top: -3rem !important;\n }\n .mr-sm-n5,\n .mx-sm-n5 {\n margin-right: -3rem !important;\n }\n .mb-sm-n5,\n .my-sm-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-sm-n5,\n .mx-sm-n5 {\n margin-left: -3rem !important;\n }\n .m-sm-auto {\n margin: auto !important;\n }\n .mt-sm-auto,\n .my-sm-auto {\n margin-top: auto !important;\n }\n .mr-sm-auto,\n .mx-sm-auto {\n margin-right: auto !important;\n }\n .mb-sm-auto,\n .my-sm-auto {\n margin-bottom: auto !important;\n }\n .ml-sm-auto,\n .mx-sm-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 768px) {\n .m-md-0 {\n margin: 0 !important;\n }\n .mt-md-0,\n .my-md-0 {\n margin-top: 0 !important;\n }\n .mr-md-0,\n .mx-md-0 {\n margin-right: 0 !important;\n }\n .mb-md-0,\n .my-md-0 {\n margin-bottom: 0 !important;\n }\n .ml-md-0,\n .mx-md-0 {\n margin-left: 0 !important;\n }\n .m-md-1 {\n margin: 0.25rem !important;\n }\n .mt-md-1,\n .my-md-1 {\n margin-top: 0.25rem !important;\n }\n .mr-md-1,\n .mx-md-1 {\n margin-right: 0.25rem !important;\n }\n .mb-md-1,\n .my-md-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-md-1,\n .mx-md-1 {\n margin-left: 0.25rem !important;\n }\n .m-md-2 {\n margin: 0.5rem !important;\n }\n .mt-md-2,\n .my-md-2 {\n margin-top: 0.5rem !important;\n }\n .mr-md-2,\n .mx-md-2 {\n margin-right: 0.5rem !important;\n }\n .mb-md-2,\n .my-md-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-md-2,\n .mx-md-2 {\n margin-left: 0.5rem !important;\n }\n .m-md-3 {\n margin: 1rem !important;\n }\n .mt-md-3,\n .my-md-3 {\n margin-top: 1rem !important;\n }\n .mr-md-3,\n .mx-md-3 {\n margin-right: 1rem !important;\n }\n .mb-md-3,\n .my-md-3 {\n margin-bottom: 1rem !important;\n }\n .ml-md-3,\n .mx-md-3 {\n margin-left: 1rem !important;\n }\n .m-md-4 {\n margin: 1.5rem !important;\n }\n .mt-md-4,\n .my-md-4 {\n margin-top: 1.5rem !important;\n }\n .mr-md-4,\n .mx-md-4 {\n margin-right: 1.5rem !important;\n }\n .mb-md-4,\n .my-md-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-md-4,\n .mx-md-4 {\n margin-left: 1.5rem !important;\n }\n .m-md-5 {\n margin: 3rem !important;\n }\n .mt-md-5,\n .my-md-5 {\n margin-top: 3rem !important;\n }\n .mr-md-5,\n .mx-md-5 {\n margin-right: 3rem !important;\n }\n .mb-md-5,\n .my-md-5 {\n margin-bottom: 3rem !important;\n }\n .ml-md-5,\n .mx-md-5 {\n margin-left: 3rem !important;\n }\n .p-md-0 {\n padding: 0 !important;\n }\n .pt-md-0,\n .py-md-0 {\n padding-top: 0 !important;\n }\n .pr-md-0,\n .px-md-0 {\n padding-right: 0 !important;\n }\n .pb-md-0,\n .py-md-0 {\n padding-bottom: 0 !important;\n }\n .pl-md-0,\n .px-md-0 {\n padding-left: 0 !important;\n }\n .p-md-1 {\n padding: 0.25rem !important;\n }\n .pt-md-1,\n .py-md-1 {\n padding-top: 0.25rem !important;\n }\n .pr-md-1,\n .px-md-1 {\n padding-right: 0.25rem !important;\n }\n .pb-md-1,\n .py-md-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-md-1,\n .px-md-1 {\n padding-left: 0.25rem !important;\n }\n .p-md-2 {\n padding: 0.5rem !important;\n }\n .pt-md-2,\n .py-md-2 {\n padding-top: 0.5rem !important;\n }\n .pr-md-2,\n .px-md-2 {\n padding-right: 0.5rem !important;\n }\n .pb-md-2,\n .py-md-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-md-2,\n .px-md-2 {\n padding-left: 0.5rem !important;\n }\n .p-md-3 {\n padding: 1rem !important;\n }\n .pt-md-3,\n .py-md-3 {\n padding-top: 1rem !important;\n }\n .pr-md-3,\n .px-md-3 {\n padding-right: 1rem !important;\n }\n .pb-md-3,\n .py-md-3 {\n padding-bottom: 1rem !important;\n }\n .pl-md-3,\n .px-md-3 {\n padding-left: 1rem !important;\n }\n .p-md-4 {\n padding: 1.5rem !important;\n }\n .pt-md-4,\n .py-md-4 {\n padding-top: 1.5rem !important;\n }\n .pr-md-4,\n .px-md-4 {\n padding-right: 1.5rem !important;\n }\n .pb-md-4,\n .py-md-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-md-4,\n .px-md-4 {\n padding-left: 1.5rem !important;\n }\n .p-md-5 {\n padding: 3rem !important;\n }\n .pt-md-5,\n .py-md-5 {\n padding-top: 3rem !important;\n }\n .pr-md-5,\n .px-md-5 {\n padding-right: 3rem !important;\n }\n .pb-md-5,\n .py-md-5 {\n padding-bottom: 3rem !important;\n }\n .pl-md-5,\n .px-md-5 {\n padding-left: 3rem !important;\n }\n .m-md-n1 {\n margin: -0.25rem !important;\n }\n .mt-md-n1,\n .my-md-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-md-n1,\n .mx-md-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-md-n1,\n .my-md-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-md-n1,\n .mx-md-n1 {\n margin-left: -0.25rem !important;\n }\n .m-md-n2 {\n margin: -0.5rem !important;\n }\n .mt-md-n2,\n .my-md-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-md-n2,\n .mx-md-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-md-n2,\n .my-md-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-md-n2,\n .mx-md-n2 {\n margin-left: -0.5rem !important;\n }\n .m-md-n3 {\n margin: -1rem !important;\n }\n .mt-md-n3,\n .my-md-n3 {\n margin-top: -1rem !important;\n }\n .mr-md-n3,\n .mx-md-n3 {\n margin-right: -1rem !important;\n }\n .mb-md-n3,\n .my-md-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-md-n3,\n .mx-md-n3 {\n margin-left: -1rem !important;\n }\n .m-md-n4 {\n margin: -1.5rem !important;\n }\n .mt-md-n4,\n .my-md-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-md-n4,\n .mx-md-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-md-n4,\n .my-md-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-md-n4,\n .mx-md-n4 {\n margin-left: -1.5rem !important;\n }\n .m-md-n5 {\n margin: -3rem !important;\n }\n .mt-md-n5,\n .my-md-n5 {\n margin-top: -3rem !important;\n }\n .mr-md-n5,\n .mx-md-n5 {\n margin-right: -3rem !important;\n }\n .mb-md-n5,\n .my-md-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-md-n5,\n .mx-md-n5 {\n margin-left: -3rem !important;\n }\n .m-md-auto {\n margin: auto !important;\n }\n .mt-md-auto,\n .my-md-auto {\n margin-top: auto !important;\n }\n .mr-md-auto,\n .mx-md-auto {\n margin-right: auto !important;\n }\n .mb-md-auto,\n .my-md-auto {\n margin-bottom: auto !important;\n }\n .ml-md-auto,\n .mx-md-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 992px) {\n .m-lg-0 {\n margin: 0 !important;\n }\n .mt-lg-0,\n .my-lg-0 {\n margin-top: 0 !important;\n }\n .mr-lg-0,\n .mx-lg-0 {\n margin-right: 0 !important;\n }\n .mb-lg-0,\n .my-lg-0 {\n margin-bottom: 0 !important;\n }\n .ml-lg-0,\n .mx-lg-0 {\n margin-left: 0 !important;\n }\n .m-lg-1 {\n margin: 0.25rem !important;\n }\n .mt-lg-1,\n .my-lg-1 {\n margin-top: 0.25rem !important;\n }\n .mr-lg-1,\n .mx-lg-1 {\n margin-right: 0.25rem !important;\n }\n .mb-lg-1,\n .my-lg-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-lg-1,\n .mx-lg-1 {\n margin-left: 0.25rem !important;\n }\n .m-lg-2 {\n margin: 0.5rem !important;\n }\n .mt-lg-2,\n .my-lg-2 {\n margin-top: 0.5rem !important;\n }\n .mr-lg-2,\n .mx-lg-2 {\n margin-right: 0.5rem !important;\n }\n .mb-lg-2,\n .my-lg-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-lg-2,\n .mx-lg-2 {\n margin-left: 0.5rem !important;\n }\n .m-lg-3 {\n margin: 1rem !important;\n }\n .mt-lg-3,\n .my-lg-3 {\n margin-top: 1rem !important;\n }\n .mr-lg-3,\n .mx-lg-3 {\n margin-right: 1rem !important;\n }\n .mb-lg-3,\n .my-lg-3 {\n margin-bottom: 1rem !important;\n }\n .ml-lg-3,\n .mx-lg-3 {\n margin-left: 1rem !important;\n }\n .m-lg-4 {\n margin: 1.5rem !important;\n }\n .mt-lg-4,\n .my-lg-4 {\n margin-top: 1.5rem !important;\n }\n .mr-lg-4,\n .mx-lg-4 {\n margin-right: 1.5rem !important;\n }\n .mb-lg-4,\n .my-lg-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-lg-4,\n .mx-lg-4 {\n margin-left: 1.5rem !important;\n }\n .m-lg-5 {\n margin: 3rem !important;\n }\n .mt-lg-5,\n .my-lg-5 {\n margin-top: 3rem !important;\n }\n .mr-lg-5,\n .mx-lg-5 {\n margin-right: 3rem !important;\n }\n .mb-lg-5,\n .my-lg-5 {\n margin-bottom: 3rem !important;\n }\n .ml-lg-5,\n .mx-lg-5 {\n margin-left: 3rem !important;\n }\n .p-lg-0 {\n padding: 0 !important;\n }\n .pt-lg-0,\n .py-lg-0 {\n padding-top: 0 !important;\n }\n .pr-lg-0,\n .px-lg-0 {\n padding-right: 0 !important;\n }\n .pb-lg-0,\n .py-lg-0 {\n padding-bottom: 0 !important;\n }\n .pl-lg-0,\n .px-lg-0 {\n padding-left: 0 !important;\n }\n .p-lg-1 {\n padding: 0.25rem !important;\n }\n .pt-lg-1,\n .py-lg-1 {\n padding-top: 0.25rem !important;\n }\n .pr-lg-1,\n .px-lg-1 {\n padding-right: 0.25rem !important;\n }\n .pb-lg-1,\n .py-lg-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-lg-1,\n .px-lg-1 {\n padding-left: 0.25rem !important;\n }\n .p-lg-2 {\n padding: 0.5rem !important;\n }\n .pt-lg-2,\n .py-lg-2 {\n padding-top: 0.5rem !important;\n }\n .pr-lg-2,\n .px-lg-2 {\n padding-right: 0.5rem !important;\n }\n .pb-lg-2,\n .py-lg-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-lg-2,\n .px-lg-2 {\n padding-left: 0.5rem !important;\n }\n .p-lg-3 {\n padding: 1rem !important;\n }\n .pt-lg-3,\n .py-lg-3 {\n padding-top: 1rem !important;\n }\n .pr-lg-3,\n .px-lg-3 {\n padding-right: 1rem !important;\n }\n .pb-lg-3,\n .py-lg-3 {\n padding-bottom: 1rem !important;\n }\n .pl-lg-3,\n .px-lg-3 {\n padding-left: 1rem !important;\n }\n .p-lg-4 {\n padding: 1.5rem !important;\n }\n .pt-lg-4,\n .py-lg-4 {\n padding-top: 1.5rem !important;\n }\n .pr-lg-4,\n .px-lg-4 {\n padding-right: 1.5rem !important;\n }\n .pb-lg-4,\n .py-lg-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-lg-4,\n .px-lg-4 {\n padding-left: 1.5rem !important;\n }\n .p-lg-5 {\n padding: 3rem !important;\n }\n .pt-lg-5,\n .py-lg-5 {\n padding-top: 3rem !important;\n }\n .pr-lg-5,\n .px-lg-5 {\n padding-right: 3rem !important;\n }\n .pb-lg-5,\n .py-lg-5 {\n padding-bottom: 3rem !important;\n }\n .pl-lg-5,\n .px-lg-5 {\n padding-left: 3rem !important;\n }\n .m-lg-n1 {\n margin: -0.25rem !important;\n }\n .mt-lg-n1,\n .my-lg-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-lg-n1,\n .mx-lg-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-lg-n1,\n .my-lg-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-lg-n1,\n .mx-lg-n1 {\n margin-left: -0.25rem !important;\n }\n .m-lg-n2 {\n margin: -0.5rem !important;\n }\n .mt-lg-n2,\n .my-lg-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-lg-n2,\n .mx-lg-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-lg-n2,\n .my-lg-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-lg-n2,\n .mx-lg-n2 {\n margin-left: -0.5rem !important;\n }\n .m-lg-n3 {\n margin: -1rem !important;\n }\n .mt-lg-n3,\n .my-lg-n3 {\n margin-top: -1rem !important;\n }\n .mr-lg-n3,\n .mx-lg-n3 {\n margin-right: -1rem !important;\n }\n .mb-lg-n3,\n .my-lg-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-lg-n3,\n .mx-lg-n3 {\n margin-left: -1rem !important;\n }\n .m-lg-n4 {\n margin: -1.5rem !important;\n }\n .mt-lg-n4,\n .my-lg-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-lg-n4,\n .mx-lg-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-lg-n4,\n .my-lg-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-lg-n4,\n .mx-lg-n4 {\n margin-left: -1.5rem !important;\n }\n .m-lg-n5 {\n margin: -3rem !important;\n }\n .mt-lg-n5,\n .my-lg-n5 {\n margin-top: -3rem !important;\n }\n .mr-lg-n5,\n .mx-lg-n5 {\n margin-right: -3rem !important;\n }\n .mb-lg-n5,\n .my-lg-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-lg-n5,\n .mx-lg-n5 {\n margin-left: -3rem !important;\n }\n .m-lg-auto {\n margin: auto !important;\n }\n .mt-lg-auto,\n .my-lg-auto {\n margin-top: auto !important;\n }\n .mr-lg-auto,\n .mx-lg-auto {\n margin-right: auto !important;\n }\n .mb-lg-auto,\n .my-lg-auto {\n margin-bottom: auto !important;\n }\n .ml-lg-auto,\n .mx-lg-auto {\n margin-left: auto !important;\n }\n}\n\n@media (min-width: 1200px) {\n .m-xl-0 {\n margin: 0 !important;\n }\n .mt-xl-0,\n .my-xl-0 {\n margin-top: 0 !important;\n }\n .mr-xl-0,\n .mx-xl-0 {\n margin-right: 0 !important;\n }\n .mb-xl-0,\n .my-xl-0 {\n margin-bottom: 0 !important;\n }\n .ml-xl-0,\n .mx-xl-0 {\n margin-left: 0 !important;\n }\n .m-xl-1 {\n margin: 0.25rem !important;\n }\n .mt-xl-1,\n .my-xl-1 {\n margin-top: 0.25rem !important;\n }\n .mr-xl-1,\n .mx-xl-1 {\n margin-right: 0.25rem !important;\n }\n .mb-xl-1,\n .my-xl-1 {\n margin-bottom: 0.25rem !important;\n }\n .ml-xl-1,\n .mx-xl-1 {\n margin-left: 0.25rem !important;\n }\n .m-xl-2 {\n margin: 0.5rem !important;\n }\n .mt-xl-2,\n .my-xl-2 {\n margin-top: 0.5rem !important;\n }\n .mr-xl-2,\n .mx-xl-2 {\n margin-right: 0.5rem !important;\n }\n .mb-xl-2,\n .my-xl-2 {\n margin-bottom: 0.5rem !important;\n }\n .ml-xl-2,\n .mx-xl-2 {\n margin-left: 0.5rem !important;\n }\n .m-xl-3 {\n margin: 1rem !important;\n }\n .mt-xl-3,\n .my-xl-3 {\n margin-top: 1rem !important;\n }\n .mr-xl-3,\n .mx-xl-3 {\n margin-right: 1rem !important;\n }\n .mb-xl-3,\n .my-xl-3 {\n margin-bottom: 1rem !important;\n }\n .ml-xl-3,\n .mx-xl-3 {\n margin-left: 1rem !important;\n }\n .m-xl-4 {\n margin: 1.5rem !important;\n }\n .mt-xl-4,\n .my-xl-4 {\n margin-top: 1.5rem !important;\n }\n .mr-xl-4,\n .mx-xl-4 {\n margin-right: 1.5rem !important;\n }\n .mb-xl-4,\n .my-xl-4 {\n margin-bottom: 1.5rem !important;\n }\n .ml-xl-4,\n .mx-xl-4 {\n margin-left: 1.5rem !important;\n }\n .m-xl-5 {\n margin: 3rem !important;\n }\n .mt-xl-5,\n .my-xl-5 {\n margin-top: 3rem !important;\n }\n .mr-xl-5,\n .mx-xl-5 {\n margin-right: 3rem !important;\n }\n .mb-xl-5,\n .my-xl-5 {\n margin-bottom: 3rem !important;\n }\n .ml-xl-5,\n .mx-xl-5 {\n margin-left: 3rem !important;\n }\n .p-xl-0 {\n padding: 0 !important;\n }\n .pt-xl-0,\n .py-xl-0 {\n padding-top: 0 !important;\n }\n .pr-xl-0,\n .px-xl-0 {\n padding-right: 0 !important;\n }\n .pb-xl-0,\n .py-xl-0 {\n padding-bottom: 0 !important;\n }\n .pl-xl-0,\n .px-xl-0 {\n padding-left: 0 !important;\n }\n .p-xl-1 {\n padding: 0.25rem !important;\n }\n .pt-xl-1,\n .py-xl-1 {\n padding-top: 0.25rem !important;\n }\n .pr-xl-1,\n .px-xl-1 {\n padding-right: 0.25rem !important;\n }\n .pb-xl-1,\n .py-xl-1 {\n padding-bottom: 0.25rem !important;\n }\n .pl-xl-1,\n .px-xl-1 {\n padding-left: 0.25rem !important;\n }\n .p-xl-2 {\n padding: 0.5rem !important;\n }\n .pt-xl-2,\n .py-xl-2 {\n padding-top: 0.5rem !important;\n }\n .pr-xl-2,\n .px-xl-2 {\n padding-right: 0.5rem !important;\n }\n .pb-xl-2,\n .py-xl-2 {\n padding-bottom: 0.5rem !important;\n }\n .pl-xl-2,\n .px-xl-2 {\n padding-left: 0.5rem !important;\n }\n .p-xl-3 {\n padding: 1rem !important;\n }\n .pt-xl-3,\n .py-xl-3 {\n padding-top: 1rem !important;\n }\n .pr-xl-3,\n .px-xl-3 {\n padding-right: 1rem !important;\n }\n .pb-xl-3,\n .py-xl-3 {\n padding-bottom: 1rem !important;\n }\n .pl-xl-3,\n .px-xl-3 {\n padding-left: 1rem !important;\n }\n .p-xl-4 {\n padding: 1.5rem !important;\n }\n .pt-xl-4,\n .py-xl-4 {\n padding-top: 1.5rem !important;\n }\n .pr-xl-4,\n .px-xl-4 {\n padding-right: 1.5rem !important;\n }\n .pb-xl-4,\n .py-xl-4 {\n padding-bottom: 1.5rem !important;\n }\n .pl-xl-4,\n .px-xl-4 {\n padding-left: 1.5rem !important;\n }\n .p-xl-5 {\n padding: 3rem !important;\n }\n .pt-xl-5,\n .py-xl-5 {\n padding-top: 3rem !important;\n }\n .pr-xl-5,\n .px-xl-5 {\n padding-right: 3rem !important;\n }\n .pb-xl-5,\n .py-xl-5 {\n padding-bottom: 3rem !important;\n }\n .pl-xl-5,\n .px-xl-5 {\n padding-left: 3rem !important;\n }\n .m-xl-n1 {\n margin: -0.25rem !important;\n }\n .mt-xl-n1,\n .my-xl-n1 {\n margin-top: -0.25rem !important;\n }\n .mr-xl-n1,\n .mx-xl-n1 {\n margin-right: -0.25rem !important;\n }\n .mb-xl-n1,\n .my-xl-n1 {\n margin-bottom: -0.25rem !important;\n }\n .ml-xl-n1,\n .mx-xl-n1 {\n margin-left: -0.25rem !important;\n }\n .m-xl-n2 {\n margin: -0.5rem !important;\n }\n .mt-xl-n2,\n .my-xl-n2 {\n margin-top: -0.5rem !important;\n }\n .mr-xl-n2,\n .mx-xl-n2 {\n margin-right: -0.5rem !important;\n }\n .mb-xl-n2,\n .my-xl-n2 {\n margin-bottom: -0.5rem !important;\n }\n .ml-xl-n2,\n .mx-xl-n2 {\n margin-left: -0.5rem !important;\n }\n .m-xl-n3 {\n margin: -1rem !important;\n }\n .mt-xl-n3,\n .my-xl-n3 {\n margin-top: -1rem !important;\n }\n .mr-xl-n3,\n .mx-xl-n3 {\n margin-right: -1rem !important;\n }\n .mb-xl-n3,\n .my-xl-n3 {\n margin-bottom: -1rem !important;\n }\n .ml-xl-n3,\n .mx-xl-n3 {\n margin-left: -1rem !important;\n }\n .m-xl-n4 {\n margin: -1.5rem !important;\n }\n .mt-xl-n4,\n .my-xl-n4 {\n margin-top: -1.5rem !important;\n }\n .mr-xl-n4,\n .mx-xl-n4 {\n margin-right: -1.5rem !important;\n }\n .mb-xl-n4,\n .my-xl-n4 {\n margin-bottom: -1.5rem !important;\n }\n .ml-xl-n4,\n .mx-xl-n4 {\n margin-left: -1.5rem !important;\n }\n .m-xl-n5 {\n margin: -3rem !important;\n }\n .mt-xl-n5,\n .my-xl-n5 {\n margin-top: -3rem !important;\n }\n .mr-xl-n5,\n .mx-xl-n5 {\n margin-right: -3rem !important;\n }\n .mb-xl-n5,\n .my-xl-n5 {\n margin-bottom: -3rem !important;\n }\n .ml-xl-n5,\n .mx-xl-n5 {\n margin-left: -3rem !important;\n }\n .m-xl-auto {\n margin: auto !important;\n }\n .mt-xl-auto,\n .my-xl-auto {\n margin-top: auto !important;\n }\n .mr-xl-auto,\n .mx-xl-auto {\n margin-right: auto !important;\n }\n .mb-xl-auto,\n .my-xl-auto {\n margin-bottom: auto !important;\n }\n .ml-xl-auto,\n .mx-xl-auto {\n margin-left: auto !important;\n }\n}\n\n.text-monospace {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace !important;\n}\n\n.text-justify {\n text-align: justify !important;\n}\n\n.text-wrap {\n white-space: normal !important;\n}\n\n.text-nowrap {\n white-space: nowrap !important;\n}\n\n.text-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.text-left {\n text-align: left !important;\n}\n\n.text-right {\n text-align: right !important;\n}\n\n.text-center {\n text-align: center !important;\n}\n\n@media (min-width: 576px) {\n .text-sm-left {\n text-align: left !important;\n }\n .text-sm-right {\n text-align: right !important;\n }\n .text-sm-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 768px) {\n .text-md-left {\n text-align: left !important;\n }\n .text-md-right {\n text-align: right !important;\n }\n .text-md-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 992px) {\n .text-lg-left {\n text-align: left !important;\n }\n .text-lg-right {\n text-align: right !important;\n }\n .text-lg-center {\n text-align: center !important;\n }\n}\n\n@media (min-width: 1200px) {\n .text-xl-left {\n text-align: left !important;\n }\n .text-xl-right {\n text-align: right !important;\n }\n .text-xl-center {\n text-align: center !important;\n }\n}\n\n.text-lowercase {\n text-transform: lowercase !important;\n}\n\n.text-uppercase {\n text-transform: uppercase !important;\n}\n\n.text-capitalize {\n text-transform: capitalize !important;\n}\n\n.font-weight-light {\n font-weight: 300 !important;\n}\n\n.font-weight-lighter {\n font-weight: lighter !important;\n}\n\n.font-weight-normal {\n font-weight: 400 !important;\n}\n\n.font-weight-bold {\n font-weight: 700 !important;\n}\n\n.font-weight-bolder {\n font-weight: bolder !important;\n}\n\n.font-italic {\n font-style: italic !important;\n}\n\n.text-white {\n color: #fff !important;\n}\n\n.text-primary {\n color: #007bff !important;\n}\n\na.text-primary:hover, a.text-primary:focus {\n color: #0056b3 !important;\n}\n\n.text-secondary {\n color: #6c757d !important;\n}\n\na.text-secondary:hover, a.text-secondary:focus {\n color: #494f54 !important;\n}\n\n.text-success {\n color: #28a745 !important;\n}\n\na.text-success:hover, a.text-success:focus {\n color: #19692c !important;\n}\n\n.text-info {\n color: #17a2b8 !important;\n}\n\na.text-info:hover, a.text-info:focus {\n color: #0f6674 !important;\n}\n\n.text-warning {\n color: #ffc107 !important;\n}\n\na.text-warning:hover, a.text-warning:focus {\n color: #ba8b00 !important;\n}\n\n.text-danger {\n color: #dc3545 !important;\n}\n\na.text-danger:hover, a.text-danger:focus {\n color: #a71d2a !important;\n}\n\n.text-light {\n color: #f8f9fa !important;\n}\n\na.text-light:hover, a.text-light:focus {\n color: #cbd3da !important;\n}\n\n.text-dark {\n color: #343a40 !important;\n}\n\na.text-dark:hover, a.text-dark:focus {\n color: #121416 !important;\n}\n\n.text-body {\n color: #212529 !important;\n}\n\n.text-muted {\n color: #6c757d !important;\n}\n\n.text-black-50 {\n color: rgba(0, 0, 0, 0.5) !important;\n}\n\n.text-white-50 {\n color: rgba(255, 255, 255, 0.5) !important;\n}\n\n.text-hide {\n font: 0/0 a;\n color: transparent;\n text-shadow: none;\n background-color: transparent;\n border: 0;\n}\n\n.text-decoration-none {\n text-decoration: none !important;\n}\n\n.text-break {\n word-break: break-word !important;\n overflow-wrap: break-word !important;\n}\n\n.text-reset {\n color: inherit !important;\n}\n\n.visible {\n visibility: visible !important;\n}\n\n.invisible {\n visibility: hidden !important;\n}\n\n@media print {\n *,\n *::before,\n *::after {\n text-shadow: none !important;\n box-shadow: none !important;\n }\n a:not(.btn) {\n text-decoration: underline;\n }\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n pre {\n white-space: pre-wrap !important;\n }\n pre,\n blockquote {\n border: 1px solid #adb5bd;\n page-break-inside: avoid;\n }\n thead {\n display: table-header-group;\n }\n tr,\n img {\n page-break-inside: avoid;\n }\n p,\n h2,\n h3 {\n orphans: 3;\n widows: 3;\n }\n h2,\n h3 {\n page-break-after: avoid;\n }\n @page {\n size: a3;\n }\n body {\n min-width: 992px !important;\n }\n .container {\n min-width: 992px !important;\n }\n .navbar {\n display: none;\n }\n .badge {\n border: 1px solid #000;\n }\n .table {\n border-collapse: collapse !important;\n }\n .table td,\n .table th {\n background-color: #fff !important;\n }\n .table-bordered th,\n .table-bordered td {\n border: 1px solid #dee2e6 !important;\n }\n .table-dark {\n color: inherit;\n }\n .table-dark th,\n .table-dark td,\n .table-dark thead th,\n .table-dark tbody + tbody {\n border-color: #dee2e6;\n }\n .table .thead-dark th {\n color: inherit;\n border-color: #dee2e6;\n }\n}\n\n/*# sourceMappingURL=bootstrap.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n","// stylelint-disable declaration-no-important, selector-list-comma-newline-after\n\n//\n// Headings\n//\n\nh1, h2, h3, h4, h5, h6,\n.h1, .h2, .h3, .h4, .h5, .h6 {\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1, .h1 { @include font-size($h1-font-size); }\nh2, .h2 { @include font-size($h2-font-size); }\nh3, .h3 { @include font-size($h3-font-size); }\nh4, .h4 { @include font-size($h4-font-size); }\nh5, .h5 { @include font-size($h5-font-size); }\nh6, .h6 { @include font-size($h6-font-size); }\n\n.lead {\n @include font-size($lead-font-size);\n font-weight: $lead-font-weight;\n}\n\n// Type display classes\n.display-1 {\n @include font-size($display1-size);\n font-weight: $display1-weight;\n line-height: $display-line-height;\n}\n.display-2 {\n @include font-size($display2-size);\n font-weight: $display2-weight;\n line-height: $display-line-height;\n}\n.display-3 {\n @include font-size($display3-size);\n font-weight: $display3-weight;\n line-height: $display-line-height;\n}\n.display-4 {\n @include font-size($display4-size);\n font-weight: $display4-weight;\n line-height: $display-line-height;\n}\n\n\n//\n// Horizontal rules\n//\n\nhr {\n margin-top: $hr-margin-y;\n margin-bottom: $hr-margin-y;\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n}\n\n\n//\n// Emphasis\n//\n\nsmall,\n.small {\n @include font-size($small-font-size);\n font-weight: $font-weight-normal;\n}\n\nmark,\n.mark {\n padding: $mark-padding;\n background-color: $mark-bg;\n}\n\n\n//\n// Lists\n//\n\n.list-unstyled {\n @include list-unstyled;\n}\n\n// Inline turns list items into inline-block\n.list-inline {\n @include list-unstyled;\n}\n.list-inline-item {\n display: inline-block;\n\n &:not(:last-child) {\n margin-right: $list-inline-padding;\n }\n}\n\n\n//\n// Misc\n//\n\n// Builds on `abbr`\n.initialism {\n @include font-size(90%);\n text-transform: uppercase;\n}\n\n// Blockquotes\n.blockquote {\n margin-bottom: $spacer;\n @include font-size($blockquote-font-size);\n}\n\n.blockquote-footer {\n display: block;\n @include font-size($blockquote-small-font-size);\n color: $blockquote-small-color;\n\n &::before {\n content: \"\\2014\\00A0\"; // em dash, nbsp\n }\n}\n","// Lists\n\n// Unstyled keeps list items block level, just removes default browser padding and list-style\n@mixin list-unstyled {\n padding-left: 0;\n list-style: none;\n}\n","// Responsive images (ensure images don't scale beyond their parents)\n//\n// This is purposefully opt-in via an explicit class rather than being the default for all ``s.\n// We previously tried the \"images are responsive by default\" approach in Bootstrap v2,\n// and abandoned it in Bootstrap v3 because it breaks lots of third-party widgets (including Google Maps)\n// which weren't expecting the images within themselves to be involuntarily resized.\n// See also https://github.com/twbs/bootstrap/issues/18178\n.img-fluid {\n @include img-fluid;\n}\n\n\n// Image thumbnails\n.img-thumbnail {\n padding: $thumbnail-padding;\n background-color: $thumbnail-bg;\n border: $thumbnail-border-width solid $thumbnail-border-color;\n @include border-radius($thumbnail-border-radius);\n @include box-shadow($thumbnail-box-shadow);\n\n // Keep them at most 100% wide\n @include img-fluid;\n}\n\n//\n// Figures\n//\n\n.figure {\n // Ensures the caption's text aligns with the image.\n display: inline-block;\n}\n\n.figure-img {\n margin-bottom: $spacer / 2;\n line-height: 1;\n}\n\n.figure-caption {\n @include font-size($figure-caption-font-size);\n color: $figure-caption-color;\n}\n","// Image Mixins\n// - Responsive image\n// - Retina image\n\n\n// Responsive image\n//\n// Keep images from scaling beyond the width of their parents.\n\n@mixin img-fluid {\n // Part 1: Set a maximum relative to the parent\n max-width: 100%;\n // Part 2: Override the height to auto, otherwise images will be stretched\n // when setting a width and height attribute on the img element.\n height: auto;\n}\n\n\n// Retina image\n//\n// Short retina mixin for setting background-image and -size.\n\n@mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) {\n background-image: url($file-1x);\n\n // Autoprefixer takes care of adding -webkit-min-device-pixel-ratio and -o-min-device-pixel-ratio,\n // but doesn't convert dppx=>dpi.\n // There's no such thing as unprefixed min-device-pixel-ratio since it's nonstandard.\n // Compatibility info: https://caniuse.com/#feat=css-media-resolution\n @media only screen and (min-resolution: 192dpi), // IE9-11 don't support dppx\n only screen and (min-resolution: 2dppx) { // Standardized\n background-image: url($file-2x);\n background-size: $width-1x $height-1x;\n }\n @include deprecate(\"`img-retina()`\", \"v4.3.0\", \"v5\");\n}\n","// stylelint-disable property-blacklist\n// Single side border-radius\n\n@mixin border-radius($radius: $border-radius, $fallback-border-radius: false) {\n @if $enable-rounded {\n border-radius: $radius;\n }\n @else if $fallback-border-radius != false {\n border-radius: $fallback-border-radius;\n }\n}\n\n@mixin border-top-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n border-bottom-left-radius: $radius;\n }\n}\n\n@mixin border-top-left-radius($radius) {\n @if $enable-rounded {\n border-top-left-radius: $radius;\n }\n}\n\n@mixin border-top-right-radius($radius) {\n @if $enable-rounded {\n border-top-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-right-radius($radius) {\n @if $enable-rounded {\n border-bottom-right-radius: $radius;\n }\n}\n\n@mixin border-bottom-left-radius($radius) {\n @if $enable-rounded {\n border-bottom-left-radius: $radius;\n }\n}\n","// Inline code\ncode {\n @include font-size($code-font-size);\n color: $code-color;\n word-break: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\n// User input typically entered via keyboard\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 @include box-shadow($kbd-box-shadow);\n\n kbd {\n padding: 0;\n @include font-size(100%);\n font-weight: $nested-kbd-font-weight;\n @include box-shadow(none);\n }\n}\n\n// Blocks of code\npre {\n display: block;\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\n// Enable scrollable blocks of code\n.pre-scrollable {\n max-height: $pre-scrollable-max-height;\n overflow-y: scroll;\n}\n","// Container widths\n//\n// Set the container width, and override it for fixed navbars in media queries.\n\n@if $enable-grid-classes {\n .container {\n @include make-container();\n @include make-container-max-widths();\n }\n}\n\n// Fluid container\n//\n// Utilizes the mixin meant for fixed width containers, but with 100% width for\n// fluid, full width layouts.\n\n@if $enable-grid-classes {\n .container-fluid {\n @include make-container();\n }\n}\n\n// Row\n//\n// Rows contain and clear the floats of your columns.\n\n@if $enable-grid-classes {\n .row {\n @include make-row();\n }\n\n // Remove the negative margin from default .row, then the horizontal padding\n // from all immediate children columns (to prevent runaway style inheritance).\n .no-gutters {\n margin-right: 0;\n margin-left: 0;\n\n > .col,\n > [class*=\"col-\"] {\n padding-right: 0;\n padding-left: 0;\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-container($gutter: $grid-gutter-width) {\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n margin-right: auto;\n margin-left: auto;\n}\n\n\n// For each breakpoint, define the maximum width of the container in a media query\n@mixin make-container-max-widths($max-widths: $container-max-widths, $breakpoints: $grid-breakpoints) {\n @each $breakpoint, $container-max-width in $max-widths {\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n max-width: $container-max-width;\n }\n }\n}\n\n@mixin make-row($gutter: $grid-gutter-width) {\n display: flex;\n flex-wrap: wrap;\n margin-right: -$gutter / 2;\n margin-left: -$gutter / 2;\n}\n\n@mixin make-col-ready($gutter: $grid-gutter-width) {\n position: relative;\n // Prevent columns from becoming too narrow when at smaller grid tiers by\n // always setting `width: 100%;`. This works because we use `flex` values\n // later on to override this initial width.\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n}\n\n@mixin make-col($size, $columns: $grid-columns) {\n flex: 0 0 percentage($size / $columns);\n // Add a `max-width` to ensure content within each column does not blow out\n // the width of the column. Applies to IE10+ and Firefox. Chrome and Safari\n // do not appear to require this.\n max-width: percentage($size / $columns);\n}\n\n@mixin make-col-offset($size, $columns: $grid-columns) {\n $num: $size / $columns;\n margin-left: if($num == 0, 0, percentage($num));\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)\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))\n// md\n// >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))\n// md\n@function breakpoint-next($name, $breakpoints: $grid-breakpoints, $breakpoint-names: map-keys($breakpoints)) {\n $n: index($breakpoint-names, $name);\n @return if($n != null and $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))\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. Null for the largest (last) breakpoint.\n// The maximum value is calculated as the minimum of the next one less 0.02px\n// to work around the limitations of `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(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\n// 767.98px\n@function breakpoint-max($name, $breakpoints: $grid-breakpoints) {\n $next: breakpoint-next($name, $breakpoints);\n @return if($next, breakpoint-min($next, $breakpoints) - .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))\n// \"\" (Returns a blank string)\n// >> breakpoint-infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))\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 $max: breakpoint-max($name, $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($name, $breakpoints) {\n @content;\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 // Common properties for all breakpoints\n %grid-column {\n position: relative;\n width: 100%;\n padding-right: $gutter / 2;\n padding-left: $gutter / 2;\n }\n\n @each $breakpoint in map-keys($breakpoints) {\n $infix: breakpoint-infix($breakpoint, $breakpoints);\n\n // Allow columns to stretch full width below their breakpoints\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @extend %grid-column;\n }\n }\n .col#{$infix},\n .col#{$infix}-auto {\n @extend %grid-column;\n }\n\n @include media-breakpoint-up($breakpoint, $breakpoints) {\n // Provide basic `.col-{bp}` classes for equal-width flexbox columns\n .col#{$infix} {\n flex-basis: 0;\n flex-grow: 1;\n max-width: 100%;\n }\n .col#{$infix}-auto {\n flex: 0 0 auto;\n width: auto;\n max-width: 100%; // Reset earlier grid tiers\n }\n\n @for $i from 1 through $columns {\n .col#{$infix}-#{$i} {\n @include make-col($i, $columns);\n }\n }\n\n .order#{$infix}-first { order: -1; }\n\n .order#{$infix}-last { order: $columns + 1; }\n\n @for $i from 0 through $columns {\n .order#{$infix}-#{$i} { order: $i; }\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}\n","//\n// Basic Bootstrap table\n//\n\n.table {\n width: 100%;\n margin-bottom: $spacer;\n color: $table-color;\n background-color: $table-bg; // Reset for nesting within parents with `background-color`.\n\n th,\n td {\n padding: $table-cell-padding;\n vertical-align: top;\n border-top: $table-border-width solid $table-border-color;\n }\n\n thead th {\n vertical-align: bottom;\n border-bottom: (2 * $table-border-width) solid $table-border-color;\n }\n\n tbody + tbody {\n border-top: (2 * $table-border-width) solid $table-border-color;\n }\n}\n\n\n//\n// Condensed table w/ half padding\n//\n\n.table-sm {\n th,\n td {\n padding: $table-cell-padding-sm;\n }\n}\n\n\n// Border versions\n//\n// Add or remove borders all around the table and between all the columns.\n\n.table-bordered {\n border: $table-border-width solid $table-border-color;\n\n th,\n td {\n border: $table-border-width solid $table-border-color;\n }\n\n thead {\n th,\n td {\n border-bottom-width: 2 * $table-border-width;\n }\n }\n}\n\n.table-borderless {\n th,\n td,\n thead th,\n tbody + tbody {\n border: 0;\n }\n}\n\n// Zebra-striping\n//\n// Default zebra-stripe styles (alternating gray and transparent backgrounds)\n\n.table-striped {\n tbody tr:nth-of-type(#{$table-striped-order}) {\n background-color: $table-accent-bg;\n }\n}\n\n\n// Hover effect\n//\n// Placed here since it has to come after the potential zebra striping\n\n.table-hover {\n tbody tr {\n @include hover {\n color: $table-hover-color;\n background-color: $table-hover-bg;\n }\n }\n}\n\n\n// Table backgrounds\n//\n// Exact selectors below required to override `.table-striped` and prevent\n// inheritance to nested tables.\n\n@each $color, $value in $theme-colors {\n @include table-row-variant($color, theme-color-level($color, $table-bg-level), theme-color-level($color, $table-border-level));\n}\n\n@include table-row-variant(active, $table-active-bg);\n\n\n// Dark styles\n//\n// Same table markup, but inverted color scheme: dark background and light text.\n\n// stylelint-disable-next-line no-duplicate-selectors\n.table {\n .thead-dark {\n th {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n border-color: $table-dark-border-color;\n }\n }\n\n .thead-light {\n th {\n color: $table-head-color;\n background-color: $table-head-bg;\n border-color: $table-border-color;\n }\n }\n}\n\n.table-dark {\n color: $table-dark-color;\n background-color: $table-dark-bg;\n\n th,\n td,\n thead th {\n border-color: $table-dark-border-color;\n }\n\n &.table-bordered {\n border: 0;\n }\n\n &.table-striped {\n tbody tr:nth-of-type(odd) {\n background-color: $table-dark-accent-bg;\n }\n }\n\n &.table-hover {\n tbody tr {\n @include hover {\n color: $table-dark-hover-color;\n background-color: $table-dark-hover-bg;\n }\n }\n }\n}\n\n\n// Responsive tables\n//\n// Generate series of `.table-responsive-*` classes for configuring the screen\n// size of where your table will overflow.\n\n.table-responsive {\n @each $breakpoint in map-keys($grid-breakpoints) {\n $next: breakpoint-next($breakpoint, $grid-breakpoints);\n $infix: breakpoint-infix($next, $grid-breakpoints);\n\n &#{$infix} {\n @include media-breakpoint-down($breakpoint) {\n display: block;\n width: 100%;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n\n // Prevent double border on horizontal scroll due to use of `display: block;`\n > .table-bordered {\n border: 0;\n }\n }\n }\n }\n}\n","// Tables\n\n@mixin table-row-variant($state, $background, $border: null) {\n // Exact selectors below required to override `.table-striped` and prevent\n // inheritance to nested tables.\n .table-#{$state} {\n &,\n > th,\n > td {\n background-color: $background;\n }\n\n @if $border != null {\n th,\n td,\n thead th,\n tbody + tbody {\n border-color: $border;\n }\n }\n }\n\n // Hover states for `.table-hover`\n // Note: this is not available for cells or rows within `thead` or `tfoot`.\n .table-hover {\n $hover-background: darken($background, 5%);\n\n .table-#{$state} {\n @include hover {\n background-color: $hover-background;\n\n > td,\n > th {\n background-color: $hover-background;\n }\n }\n }\n }\n}\n","// stylelint-disable selector-no-qualifying-type\n\n//\n// Textual form controls\n//\n\n.form-control {\n display: block;\n width: 100%;\n height: $input-height;\n padding: $input-padding-y $input-padding-x;\n font-family: $input-font-family;\n @include font-size($input-font-size);\n font-weight: $input-font-weight;\n line-height: $input-line-height;\n color: $input-color;\n background-color: $input-bg;\n background-clip: padding-box;\n border: $input-border-width solid $input-border-color;\n\n // Note: This has no effect on `s in CSS.\n @include border-radius($input-border-radius, 0);\n\n @include box-shadow($input-box-shadow);\n @include transition($input-transition);\n\n // Unstyle the caret on ` receives focus\n // in IE and (under certain conditions) Edge, as it looks bad and cannot be made to\n // match the appearance of the native widget.\n // See https://github.com/twbs/bootstrap/issues/19398.\n color: $input-color;\n background-color: $input-bg;\n }\n}\n\n// Make file inputs better match text inputs by forcing them to new lines.\n.form-control-file,\n.form-control-range {\n display: block;\n width: 100%;\n}\n\n\n//\n// Labels\n//\n\n// For use with horizontal and inline forms, when you need the label (or legend)\n// text to align with the form controls.\n.col-form-label {\n padding-top: calc(#{$input-padding-y} + #{$input-border-width});\n padding-bottom: calc(#{$input-padding-y} + #{$input-border-width});\n margin-bottom: 0; // Override the `