From 491b934413dc31874bebee17db7843ac69f7e493 Mon Sep 17 00:00:00 2001 From: Adrian Mesa Sacasas Date: Wed, 15 Oct 2025 13:49:39 -0400 Subject: [PATCH 1/4] fix: error en los modelos de configuracion de datos que causaba error al aplicar migraciones feat: agregado el cliente web, estructura basica del layout principal chore: limpieza y cambios menores en el codigo --- OTManager.Data/Audites/Audited.cs | 4 +- OTManager.Data/Configs/ClientConfig.cs | 1 + OTManager.Data/Configs/FactureConfig.cs | 1 + OTManager.Data/Configs/MaterialConfig.cs | 1 + OTManager.Data/Configs/MaterialCostConfig.cs | 1 + OTManager.Data/Configs/WorkerConfig.cs | 2 +- OTManager.Data/Configs/WorkerCostConfig.cs | 6 +- .../Context/ApplicationDbContext.cs | 2 +- .../20251009232432_InitialCommit.cs | 11 +- ...10212249_Models&ConfigsUpdates.Designer.cs | 556 +++++++++++++ .../20251010212249_Models&ConfigsUpdates.cs | 777 ++++++++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 109 +-- .../Implements/ClientRepository.cs | 1 + .../Implements/FactureRepository.cs | 1 + .../Implements/MaterialCostRepository.cs | 1 + .../Implements/MaterialRepository.cs | 1 + .../Implements/OrderRepository.cs | 1 + .../Implements/OrderServiceRepository.cs | 1 + .../Implements/ServiceCostRepository.cs | 1 + .../Implements/WorkerCostRepository.cs | 1 + .../Implements/WorkerRepository.cs | 1 + .../Repositories/RepositoryExtension.cs | 1 + OTManager.Data/UoW/UnitOfWork.cs | 3 +- OTManager.Wapi.sln | 7 + .../Controllers/MaterialController.cs | 10 +- .../Controllers/ServiceOrderController.cs | 10 +- .../CreateOrderServiceEndpoint.cs | 30 - .../DeleteOrderServiceEndpoint.cs | 39 - .../GetOrderServiceByIdEndpoint.cs | 39 - .../GetOrderServiceEndpoint.cs | 27 - .../UpdateOrderServiceEndpoint.cs | 42 - .../ApplicationDependencyInjection.cs | 10 +- .../Extensions/DependencyInjection.cs | 24 - src/OTManager.Api/OTManager.Api.csproj | 1 - src/OTManager.Api/Program.cs | 4 +- .../Dtos/Clients/ReadClientDto.cs | 1 - src/OTManager.App/Mappers/MapperExtension.cs | 23 + .../Implements/OrderServiceService.cs | 2 +- ...eService.cs => IOrderServiceAppService.cs} | 0 .../Services/ServiceExtension.cs | 13 +- .../Entities/OT/MaterialCost.cs | 16 +- src/OTManager.Core/Entities/OT/ServiceCost.cs | 16 +- src/OTManager.Core/Entities/OT/WorkerCost.cs | 16 +- src/OTManager.Web/Components/App.razor | 23 + .../Components/ClientListViewComponent.razor | 1 + .../Clients/Pages/ClientsIndex.razor | 5 + .../Components/Clients/_Imports.razor | 1 + .../Dashboards/Pages/DashboardIndex.razor | 7 + .../Components/Layout/MainLayout.razor | 33 + .../Components/Layout/NavMenu.razor | 27 + .../MaterialListViewComponent.razor | 41 + .../Materials/Pages/StocksIndex.razor | 44 + .../Materials/Pages/StocksIndex.razor.cs | 29 + .../Components/Materials/_Imports.razor | 1 + .../Components/Pages/Error.razor | 36 + src/OTManager.Web/Components/Pages/Home.razor | 7 + src/OTManager.Web/Components/Routes.razor | 6 + .../Localization/CultureController.cs | 21 + .../Settings/Layout/SettingsLayout.razor | 48 ++ .../Settings/Pages/SettingDataPage.razor | 107 +++ .../Settings/Pages/SettingLanguagePage.razor | 85 ++ .../Settings/Pages/SettingThemePage.razor | 82 ++ .../Settings/Pages/SettingUserPage.razor | 40 + .../Settings/Pages/SettingUserPage.razor.cs | 20 + .../Settings/Pages/SettingsPage.razor | 33 + .../Components/Settings/Pages/_Imports.razor | 6 + .../Resources/Enums/LocalizerSelect.cs | 11 + .../Settings/Resources/Enums/StyleColor.cs | 20 + .../Localization/UiLanguage.Designer.cs | 126 +++ .../Localization/UiLanguage.en-US.resx | 141 ++++ .../Localization/UiLanguage.es-ES.resx | 141 ++++ .../Resources/Localization/UiLanguage.resx | 143 ++++ .../Components/Settings/_Imports.razor | 0 src/OTManager.Web/Components/_Imports.razor | 12 + src/OTManager.Web/OTManager.Web.csproj | 13 + src/OTManager.Web/Program.cs | 44 + .../Properties/launchSettings.json | 23 + .../appsettings.Development.json | 8 + src/OTManager.Web/appsettings.json | 9 + src/OTManager.Web/wwwroot/app.css | 191 +++++ 80 files changed, 3091 insertions(+), 308 deletions(-) create mode 100644 OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.Designer.cs create mode 100644 OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs delete mode 100644 src/OTManager.Api/Endpoints/OrderServiceEndpoints/CreateOrderServiceEndpoint.cs delete mode 100644 src/OTManager.Api/Endpoints/OrderServiceEndpoints/DeleteOrderServiceEndpoint.cs delete mode 100644 src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceByIdEndpoint.cs delete mode 100644 src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceEndpoint.cs delete mode 100644 src/OTManager.Api/Endpoints/OrderServiceEndpoints/UpdateOrderServiceEndpoint.cs delete mode 100644 src/OTManager.Api/Extensions/DependencyInjection.cs create mode 100644 src/OTManager.App/Mappers/MapperExtension.cs rename src/OTManager.App/Services/Interfaces/{IOrderServiceService.cs => IOrderServiceAppService.cs} (100%) create mode 100644 src/OTManager.Web/Components/App.razor create mode 100644 src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor create mode 100644 src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor create mode 100644 src/OTManager.Web/Components/Clients/_Imports.razor create mode 100644 src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor create mode 100644 src/OTManager.Web/Components/Layout/MainLayout.razor create mode 100644 src/OTManager.Web/Components/Layout/NavMenu.razor create mode 100644 src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor create mode 100644 src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor create mode 100644 src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs create mode 100644 src/OTManager.Web/Components/Materials/_Imports.razor create mode 100644 src/OTManager.Web/Components/Pages/Error.razor create mode 100644 src/OTManager.Web/Components/Pages/Home.razor create mode 100644 src/OTManager.Web/Components/Routes.razor create mode 100644 src/OTManager.Web/Components/Settings/Controllers/Localization/CultureController.cs create mode 100644 src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor create mode 100644 src/OTManager.Web/Components/Settings/Pages/SettingDataPage.razor create mode 100644 src/OTManager.Web/Components/Settings/Pages/SettingLanguagePage.razor create mode 100644 src/OTManager.Web/Components/Settings/Pages/SettingThemePage.razor create mode 100644 src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor create mode 100644 src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs create mode 100644 src/OTManager.Web/Components/Settings/Pages/SettingsPage.razor create mode 100644 src/OTManager.Web/Components/Settings/Pages/_Imports.razor create mode 100644 src/OTManager.Web/Components/Settings/Resources/Enums/LocalizerSelect.cs create mode 100644 src/OTManager.Web/Components/Settings/Resources/Enums/StyleColor.cs create mode 100644 src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.Designer.cs create mode 100644 src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.en-US.resx create mode 100644 src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.es-ES.resx create mode 100644 src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.resx create mode 100644 src/OTManager.Web/Components/Settings/_Imports.razor create mode 100644 src/OTManager.Web/Components/_Imports.razor create mode 100644 src/OTManager.Web/OTManager.Web.csproj create mode 100644 src/OTManager.Web/Program.cs create mode 100644 src/OTManager.Web/Properties/launchSettings.json create mode 100644 src/OTManager.Web/appsettings.Development.json create mode 100644 src/OTManager.Web/appsettings.json create mode 100644 src/OTManager.Web/wwwroot/app.css diff --git a/OTManager.Data/Audites/Audited.cs b/OTManager.Data/Audites/Audited.cs index ad926d8..9a263a0 100644 --- a/OTManager.Data/Audites/Audited.cs +++ b/OTManager.Data/Audites/Audited.cs @@ -1,7 +1,5 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; -using OTManager.Core.Entities.Abstracts; -using System; namespace OTManager.Data.Audites; @@ -51,4 +49,4 @@ public static void AplicarAuditoria(ChangeTracker changeTracker, string userName } } -public interface IAuditableEntity {} +public interface IAuditableEntity { } diff --git a/OTManager.Data/Configs/ClientConfig.cs b/OTManager.Data/Configs/ClientConfig.cs index 5cbf4b8..90b5f8f 100644 --- a/OTManager.Data/Configs/ClientConfig.cs +++ b/OTManager.Data/Configs/ClientConfig.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; + using OTManager.Core.Entities.OT; namespace OTManager.Data.Configs; diff --git a/OTManager.Data/Configs/FactureConfig.cs b/OTManager.Data/Configs/FactureConfig.cs index 1fd709b..b376b81 100644 --- a/OTManager.Data/Configs/FactureConfig.cs +++ b/OTManager.Data/Configs/FactureConfig.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; + using OTManager.Core.Entities.OT; namespace OTManager.Data.Configs; diff --git a/OTManager.Data/Configs/MaterialConfig.cs b/OTManager.Data/Configs/MaterialConfig.cs index 9225f41..0354d9d 100644 --- a/OTManager.Data/Configs/MaterialConfig.cs +++ b/OTManager.Data/Configs/MaterialConfig.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; + using OTManager.Core.Entities.OT; namespace OTManager.Data.Configs; diff --git a/OTManager.Data/Configs/MaterialCostConfig.cs b/OTManager.Data/Configs/MaterialCostConfig.cs index 8933c40..99b7e3c 100644 --- a/OTManager.Data/Configs/MaterialCostConfig.cs +++ b/OTManager.Data/Configs/MaterialCostConfig.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; + using OTManager.Core.Entities.OT; namespace OTManager.Data.Configs; diff --git a/OTManager.Data/Configs/WorkerConfig.cs b/OTManager.Data/Configs/WorkerConfig.cs index 2e067be..5446568 100644 --- a/OTManager.Data/Configs/WorkerConfig.cs +++ b/OTManager.Data/Configs/WorkerConfig.cs @@ -14,6 +14,6 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(s => s.Name); builder.HasIndex(s => s.CreatedAt); - builder.Property(s => s.HourlyRate).HasColumnType("decimal(2,4)").HasDefaultValue(0.00); + builder.Property(s => s.HourlyRate).HasColumnType("decimal(2,2)").HasDefaultValue(0.00); } } diff --git a/OTManager.Data/Configs/WorkerCostConfig.cs b/OTManager.Data/Configs/WorkerCostConfig.cs index 8407e9d..d8fe4aa 100644 --- a/OTManager.Data/Configs/WorkerCostConfig.cs +++ b/OTManager.Data/Configs/WorkerCostConfig.cs @@ -14,8 +14,8 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(s => s.Name); builder.HasIndex(s => s.CreatedAt); - builder.Property(s => s.HourlyRate).HasColumnType("decimal(2,4)").HasDefaultValue(0.00); - builder.Property(s => s.HoursWorked).HasColumnType("decimal(2,4)").HasDefaultValue(0.00); - builder.Property(s => s.TotalCost).HasColumnType("decimal(2,4)").HasDefaultValue(0.00); + builder.Property(s => s.HourlyRate).HasColumnType("decimal(2,2)").HasDefaultValue(0.00); + builder.Property(s => s.HoursWorked).HasColumnType("decimal(2,2)").HasDefaultValue(0.00); + builder.Property(s => s.TotalCost).HasColumnType("decimal(2,2)").HasDefaultValue(0.00); } } diff --git a/OTManager.Data/Context/ApplicationDbContext.cs b/OTManager.Data/Context/ApplicationDbContext.cs index 78f6e19..0f01fc5 100644 --- a/OTManager.Data/Context/ApplicationDbContext.cs +++ b/OTManager.Data/Context/ApplicationDbContext.cs @@ -1,7 +1,7 @@ using Microsoft.EntityFrameworkCore; -using OTManager.Data.Audites; using OTManager.Core.Entities.OT; +using OTManager.Data.Audites; namespace OTManager.Data.Context; diff --git a/OTManager.Data/Migrations/20251009232432_InitialCommit.cs b/OTManager.Data/Migrations/20251009232432_InitialCommit.cs index 383862f..5eb8e6a 100644 --- a/OTManager.Data/Migrations/20251009232432_InitialCommit.cs +++ b/OTManager.Data/Migrations/20251009232432_InitialCommit.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable @@ -71,7 +70,7 @@ protected override void Up(MigrationBuilder migrationBuilder) { Id = table.Column(type: "uniqueidentifier", nullable: false), Name = table.Column(type: "nvarchar(450)", nullable: false), - HourlyRate = table.Column(type: "decimal(2,4)", nullable: false, defaultValue: 0m), + HourlyRate = table.Column(type: "decimal(2,2)", nullable: false, defaultValue: 0m), CreatedAt = table.Column(type: "datetime2", nullable: true), CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), UpdatedAt = table.Column(type: "datetime2", nullable: true), @@ -200,10 +199,10 @@ protected override void Up(MigrationBuilder migrationBuilder) { Id = table.Column(type: "uniqueidentifier", nullable: false), Name = table.Column(type: "nvarchar(450)", nullable: false), - HourlyRate = table.Column(type: "decimal(2,4)", nullable: false, defaultValue: 0m), + HourlyRate = table.Column(type: "decimal(2,2)", nullable: false, defaultValue: 0m), OrderId = table.Column(type: "uniqueidentifier", nullable: false), - HoursWorked = table.Column(type: "decimal(2,4)", nullable: false, defaultValue: 0m), - TotalCost = table.Column(type: "decimal(2,4)", nullable: false, defaultValue: 0m), + HoursWorked = table.Column(type: "decimal(2,2)", nullable: false, defaultValue: 0m), + TotalCost = table.Column(type: "decimal(2,2)", nullable: false, defaultValue: 0m), CreatedAt = table.Column(type: "datetime2", nullable: true), CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), UpdatedAt = table.Column(type: "datetime2", nullable: true), diff --git a/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.Designer.cs b/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.Designer.cs new file mode 100644 index 0000000..99709e9 --- /dev/null +++ b/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.Designer.cs @@ -0,0 +1,556 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using OTManager.Data.Context; + +#nullable disable + +namespace OTManager.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251010212249_Models&ConfigsUpdates")] + partial class ModelsConfigsUpdates + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Name"); + + b.ToTable("Client", (string)null); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("TotalPrice") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,2)") + .HasDefaultValue(0.00m); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedAt"); + + b.HasIndex("OrderId"); + + b.ToTable("Facture", (string)null); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Material", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MeasureUnit") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("UnitCost") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,2)") + .HasDefaultValue(0.00m); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Name"); + + b.ToTable("Material", (string)null); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.MaterialCost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("MeasureUnit") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,6)") + .HasDefaultValue(0.00m); + + b.Property("TotalCost") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,2)") + .HasDefaultValue(0.00m); + + b.Property("UnitCost") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,2)") + .HasDefaultValue(0.00m); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Name"); + + b.HasIndex("OrderId"); + + b.ToTable("MaterialCost", (string)null); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ClientId") + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("OrderNumber") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("nvarchar(10)"); + + b.Property("OrderType") + .HasColumnType("int"); + + b.Property("Status") + .HasColumnType("bit"); + + b.Property("TotalCost") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,2)") + .HasDefaultValue(0.00m); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("OrderNumber") + .IsUnique(); + + b.ToTable("Order", (string)null); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceCost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("Price") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,2)") + .HasDefaultValue(0m); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Name"); + + b.HasIndex("OrderId"); + + b.ToTable("ServiceCosts"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceOrder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Price") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(18,2)") + .HasDefaultValue(0m); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Name"); + + b.ToTable("Services"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Worker", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("HourlyRate") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(2,2)") + .HasDefaultValue(0m); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Name"); + + b.ToTable("Workers"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.WorkerCost", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetime2"); + + b.Property("CreatedBy") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("HourlyRate") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(2,2)") + .HasDefaultValue(0m); + + b.Property("HoursWorked") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(2,2)") + .HasDefaultValue(0m); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + + b.Property("TotalCost") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(2,2)") + .HasDefaultValue(0m); + + b.Property("UpdatedAt") + .HasColumnType("datetime2"); + + b.Property("UpdatedBy") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("Name"); + + b.HasIndex("OrderId"); + + b.ToTable("WorkerCosts"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Facture", b => + { + b.HasOne("OTManager.Core.Entities.OT.Client", "Client") + .WithMany("Factures") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("OTManager.Core.Entities.OT.Order", "Order") + .WithMany() + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Client"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.MaterialCost", b => + { + b.HasOne("OTManager.Core.Entities.OT.Order", "Order") + .WithMany("Materials") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Order", b => + { + b.HasOne("OTManager.Core.Entities.OT.Client", "Client") + .WithMany("Orders") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Client"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceCost", b => + { + b.HasOne("OTManager.Core.Entities.OT.Order", "Order") + .WithMany("Services") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.WorkerCost", b => + { + b.HasOne("OTManager.Core.Entities.OT.Order", "Order") + .WithMany("Workers") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Client", b => + { + b.Navigation("Factures"); + + b.Navigation("Orders"); + }); + + modelBuilder.Entity("OTManager.Core.Entities.OT.Order", b => + { + b.Navigation("Materials"); + + b.Navigation("Services"); + + b.Navigation("Workers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs b/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs new file mode 100644 index 0000000..3176273 --- /dev/null +++ b/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs @@ -0,0 +1,777 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace OTManager.Data.Migrations +{ + /// + public partial class ModelsConfigsUpdates : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Features_Clients_ClientId", + table: "Features"); + + migrationBuilder.DropForeignKey( + name: "FK_Features_Orders_OrderId", + table: "Features"); + + migrationBuilder.DropForeignKey( + name: "FK_MaterialCosts_Orders_OrderId", + table: "MaterialCosts"); + + migrationBuilder.DropForeignKey( + name: "FK_Orders_Clients_ClientId", + table: "Orders"); + + migrationBuilder.DropForeignKey( + name: "FK_ServiceCosts_Orders_OrderId", + table: "ServiceCosts"); + + migrationBuilder.DropForeignKey( + name: "FK_WorkerCosts_Orders_OrderId", + table: "WorkerCosts"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Orders", + table: "Orders"); + + migrationBuilder.DropIndex( + name: "IX_Orders_OrderNumber", + table: "Orders"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Materials", + table: "Materials"); + + migrationBuilder.DropIndex( + name: "IX_Materials_Code", + table: "Materials"); + + migrationBuilder.DropPrimaryKey( + name: "PK_MaterialCosts", + table: "MaterialCosts"); + + migrationBuilder.DropIndex( + name: "IX_MaterialCosts_Code", + table: "MaterialCosts"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Features", + table: "Features"); + + migrationBuilder.DropIndex( + name: "IX_Features_Code", + table: "Features"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Clients", + table: "Clients"); + + migrationBuilder.DropIndex( + name: "IX_Clients_Code", + table: "Clients"); + + migrationBuilder.RenameTable( + name: "Orders", + newName: "Order"); + + migrationBuilder.RenameTable( + name: "Materials", + newName: "Material"); + + migrationBuilder.RenameTable( + name: "MaterialCosts", + newName: "MaterialCost"); + + migrationBuilder.RenameTable( + name: "Features", + newName: "Facture"); + + migrationBuilder.RenameTable( + name: "Clients", + newName: "Client"); + + migrationBuilder.RenameIndex( + name: "IX_Orders_CreatedAt", + table: "Order", + newName: "IX_Order_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_Orders_ClientId", + table: "Order", + newName: "IX_Order_ClientId"); + + migrationBuilder.RenameIndex( + name: "IX_Materials_Name", + table: "Material", + newName: "IX_Material_Name"); + + migrationBuilder.RenameIndex( + name: "IX_Materials_CreatedAt", + table: "Material", + newName: "IX_Material_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_MaterialCosts_OrderId", + table: "MaterialCost", + newName: "IX_MaterialCost_OrderId"); + + migrationBuilder.RenameIndex( + name: "IX_MaterialCosts_Name", + table: "MaterialCost", + newName: "IX_MaterialCost_Name"); + + migrationBuilder.RenameIndex( + name: "IX_MaterialCosts_CreatedAt", + table: "MaterialCost", + newName: "IX_MaterialCost_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_Features_OrderId", + table: "Facture", + newName: "IX_Facture_OrderId"); + + migrationBuilder.RenameIndex( + name: "IX_Features_CreatedAt", + table: "Facture", + newName: "IX_Facture_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_Features_ClientId", + table: "Facture", + newName: "IX_Facture_ClientId"); + + migrationBuilder.RenameIndex( + name: "IX_Clients_Name", + table: "Client", + newName: "IX_Client_Name"); + + migrationBuilder.RenameIndex( + name: "IX_Clients_CreatedAt", + table: "Client", + newName: "IX_Client_CreatedAt"); + + migrationBuilder.AlterColumn( + name: "HourlyRate", + table: "Workers", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "TotalCost", + table: "WorkerCosts", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "HoursWorked", + table: "WorkerCosts", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "HourlyRate", + table: "WorkerCosts", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Order", + type: "nvarchar(100)", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Order", + type: "nvarchar(100)", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Material", + type: "nvarchar(100)", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Material", + type: "nvarchar(100)", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "MaterialCost", + type: "nvarchar(100)", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "MaterialCost", + type: "nvarchar(100)", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Facture", + type: "nvarchar(100)", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Facture", + type: "nvarchar(100)", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Client", + type: "nvarchar(100)", + maxLength: 100, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Client", + type: "nvarchar(100)", + maxLength: 100, + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(max)"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Order", + table: "Order", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Material", + table: "Material", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_MaterialCost", + table: "MaterialCost", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Facture", + table: "Facture", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Client", + table: "Client", + column: "Id"); + + migrationBuilder.CreateIndex( + name: "IX_Order_OrderNumber", + table: "Order", + column: "OrderNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Material_Code", + table: "Material", + column: "Code", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_MaterialCost_Code", + table: "MaterialCost", + column: "Code", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Facture_Code", + table: "Facture", + column: "Code", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Client_Code", + table: "Client", + column: "Code", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_Facture_Client_ClientId", + table: "Facture", + column: "ClientId", + principalTable: "Client", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Facture_Order_OrderId", + table: "Facture", + column: "OrderId", + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_MaterialCost_Order_OrderId", + table: "MaterialCost", + column: "OrderId", + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Order_Client_ClientId", + table: "Order", + column: "ClientId", + principalTable: "Client", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ServiceCosts_Order_OrderId", + table: "ServiceCosts", + column: "OrderId", + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_WorkerCosts_Order_OrderId", + table: "WorkerCosts", + column: "OrderId", + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Facture_Client_ClientId", + table: "Facture"); + + migrationBuilder.DropForeignKey( + name: "FK_Facture_Order_OrderId", + table: "Facture"); + + migrationBuilder.DropForeignKey( + name: "FK_MaterialCost_Order_OrderId", + table: "MaterialCost"); + + migrationBuilder.DropForeignKey( + name: "FK_Order_Client_ClientId", + table: "Order"); + + migrationBuilder.DropForeignKey( + name: "FK_ServiceCosts_Order_OrderId", + table: "ServiceCosts"); + + migrationBuilder.DropForeignKey( + name: "FK_WorkerCosts_Order_OrderId", + table: "WorkerCosts"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Order", + table: "Order"); + + migrationBuilder.DropIndex( + name: "IX_Order_OrderNumber", + table: "Order"); + + migrationBuilder.DropPrimaryKey( + name: "PK_MaterialCost", + table: "MaterialCost"); + + migrationBuilder.DropIndex( + name: "IX_MaterialCost_Code", + table: "MaterialCost"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Material", + table: "Material"); + + migrationBuilder.DropIndex( + name: "IX_Material_Code", + table: "Material"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Facture", + table: "Facture"); + + migrationBuilder.DropIndex( + name: "IX_Facture_Code", + table: "Facture"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Client", + table: "Client"); + + migrationBuilder.DropIndex( + name: "IX_Client_Code", + table: "Client"); + + migrationBuilder.RenameTable( + name: "Order", + newName: "Orders"); + + migrationBuilder.RenameTable( + name: "MaterialCost", + newName: "MaterialCosts"); + + migrationBuilder.RenameTable( + name: "Material", + newName: "Materials"); + + migrationBuilder.RenameTable( + name: "Facture", + newName: "Features"); + + migrationBuilder.RenameTable( + name: "Client", + newName: "Clients"); + + migrationBuilder.RenameIndex( + name: "IX_Order_CreatedAt", + table: "Orders", + newName: "IX_Orders_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_Order_ClientId", + table: "Orders", + newName: "IX_Orders_ClientId"); + + migrationBuilder.RenameIndex( + name: "IX_MaterialCost_OrderId", + table: "MaterialCosts", + newName: "IX_MaterialCosts_OrderId"); + + migrationBuilder.RenameIndex( + name: "IX_MaterialCost_Name", + table: "MaterialCosts", + newName: "IX_MaterialCosts_Name"); + + migrationBuilder.RenameIndex( + name: "IX_MaterialCost_CreatedAt", + table: "MaterialCosts", + newName: "IX_MaterialCosts_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_Material_Name", + table: "Materials", + newName: "IX_Materials_Name"); + + migrationBuilder.RenameIndex( + name: "IX_Material_CreatedAt", + table: "Materials", + newName: "IX_Materials_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_Facture_OrderId", + table: "Features", + newName: "IX_Features_OrderId"); + + migrationBuilder.RenameIndex( + name: "IX_Facture_CreatedAt", + table: "Features", + newName: "IX_Features_CreatedAt"); + + migrationBuilder.RenameIndex( + name: "IX_Facture_ClientId", + table: "Features", + newName: "IX_Features_ClientId"); + + migrationBuilder.RenameIndex( + name: "IX_Client_Name", + table: "Clients", + newName: "IX_Clients_Name"); + + migrationBuilder.RenameIndex( + name: "IX_Client_CreatedAt", + table: "Clients", + newName: "IX_Clients_CreatedAt"); + + migrationBuilder.AlterColumn( + name: "HourlyRate", + table: "Workers", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "TotalCost", + table: "WorkerCosts", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "HoursWorked", + table: "WorkerCosts", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "HourlyRate", + table: "WorkerCosts", + type: "decimal(2,2)", + nullable: false, + defaultValue: 0m, + oldClrType: typeof(decimal), + oldType: "decimal(2,2)", + oldDefaultValue: 0m); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Orders", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Orders", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "MaterialCosts", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "MaterialCosts", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Materials", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Materials", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Features", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Features", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100); + + migrationBuilder.AlterColumn( + name: "UpdatedBy", + table: "Clients", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100, + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedBy", + table: "Clients", + type: "nvarchar(max)", + nullable: false, + oldClrType: typeof(string), + oldType: "nvarchar(100)", + oldMaxLength: 100); + + migrationBuilder.AddPrimaryKey( + name: "PK_Orders", + table: "Orders", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_MaterialCosts", + table: "MaterialCosts", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Materials", + table: "Materials", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Features", + table: "Features", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Clients", + table: "Clients", + column: "Id"); + + migrationBuilder.CreateIndex( + name: "IX_Orders_OrderNumber", + table: "Orders", + column: "OrderNumber"); + + migrationBuilder.CreateIndex( + name: "IX_MaterialCosts_Code", + table: "MaterialCosts", + column: "Code"); + + migrationBuilder.CreateIndex( + name: "IX_Materials_Code", + table: "Materials", + column: "Code"); + + migrationBuilder.CreateIndex( + name: "IX_Features_Code", + table: "Features", + column: "Code"); + + migrationBuilder.CreateIndex( + name: "IX_Clients_Code", + table: "Clients", + column: "Code"); + + migrationBuilder.AddForeignKey( + name: "FK_Features_Clients_ClientId", + table: "Features", + column: "ClientId", + principalTable: "Clients", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Features_Orders_OrderId", + table: "Features", + column: "OrderId", + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_MaterialCosts_Orders_OrderId", + table: "MaterialCosts", + column: "OrderId", + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Orders_Clients_ClientId", + table: "Orders", + column: "ClientId", + principalTable: "Clients", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ServiceCosts_Orders_OrderId", + table: "ServiceCosts", + column: "OrderId", + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_WorkerCosts_Orders_OrderId", + table: "WorkerCosts", + column: "OrderId", + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs index d12d53e..ed707d4 100644 --- a/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -38,7 +38,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedBy") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("Name") .IsRequired() @@ -49,17 +50,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("datetime2"); b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.HasKey("Id"); - b.HasIndex("Code"); + b.HasIndex("Code") + .IsUnique(); b.HasIndex("CreatedAt"); b.HasIndex("Name"); - b.ToTable("Clients"); + b.ToTable("Client", (string)null); }); modelBuilder.Entity("OTManager.Core.Entities.OT.Facture", b => @@ -81,7 +84,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedBy") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("OrderId") .HasColumnType("uniqueidentifier"); @@ -89,25 +93,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TotalPrice") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); + .HasDefaultValue(0.00m); b.Property("UpdatedAt") .HasColumnType("datetime2"); b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.HasKey("Id"); b.HasIndex("ClientId"); - b.HasIndex("Code"); + b.HasIndex("Code") + .IsUnique(); b.HasIndex("CreatedAt"); b.HasIndex("OrderId"); - b.ToTable("Features"); + b.ToTable("Facture", (string)null); }); modelBuilder.Entity("OTManager.Core.Entities.OT.Material", b => @@ -126,7 +132,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedBy") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("MeasureUnit") .IsRequired() @@ -139,23 +146,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UnitCost") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); + .HasDefaultValue(0.00m); b.Property("UpdatedAt") .HasColumnType("datetime2"); b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.HasKey("Id"); - b.HasIndex("Code"); + b.HasIndex("Code") + .IsUnique(); b.HasIndex("CreatedAt"); b.HasIndex("Name"); - b.ToTable("Materials"); + b.ToTable("Material", (string)null); }); modelBuilder.Entity("OTManager.Core.Entities.OT.MaterialCost", b => @@ -174,7 +183,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedBy") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("MeasureUnit") .IsRequired() @@ -190,27 +200,29 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Quantity") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,6)") - .HasDefaultValue(0m); + .HasDefaultValue(0.00m); b.Property("TotalCost") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); + .HasDefaultValue(0.00m); b.Property("UnitCost") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); + .HasDefaultValue(0.00m); b.Property("UpdatedAt") .HasColumnType("datetime2"); b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.HasKey("Id"); - b.HasIndex("Code"); + b.HasIndex("Code") + .IsUnique(); b.HasIndex("CreatedAt"); @@ -218,7 +230,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("OrderId"); - b.ToTable("MaterialCosts"); + b.ToTable("MaterialCost", (string)null); }); modelBuilder.Entity("OTManager.Core.Entities.OT.Order", b => @@ -235,7 +247,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedBy") .IsRequired() - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.Property("OrderNumber") .IsRequired() @@ -251,13 +264,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TotalCost") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); + .HasDefaultValue(0.00m); b.Property("UpdatedAt") .HasColumnType("datetime2"); b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); b.HasKey("Id"); @@ -265,12 +279,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CreatedAt"); - b.HasIndex("OrderNumber"); + b.HasIndex("OrderNumber") + .IsUnique(); - b.ToTable("Orders"); + b.ToTable("Order", (string)null); }); - modelBuilder.Entity("OTManager.Core.Entities.OT.OrderService", b => + modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceCost", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -291,11 +306,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(450)"); + b.Property("OrderId") + .HasColumnType("uniqueidentifier"); + b.Property("Price") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,2)") .HasDefaultValue(0m); + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1); + + b.Property("TotalPrice") + .HasColumnType("decimal(18,2)"); + b.Property("UpdatedAt") .HasColumnType("datetime2"); @@ -308,10 +334,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Name"); - b.ToTable("Services"); + b.HasIndex("OrderId"); + + b.ToTable("ServiceCosts"); }); - modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceCost", b => + modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceOrder", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -332,22 +360,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("nvarchar(450)"); - b.Property("OrderId") - .HasColumnType("uniqueidentifier"); - b.Property("Price") .ValueGeneratedOnAdd() .HasColumnType("decimal(18,2)") .HasDefaultValue(0m); - b.Property("Quantity") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(1); - - b.Property("TotalPrice") - .HasColumnType("decimal(18,2)"); - b.Property("UpdatedAt") .HasColumnType("datetime2"); @@ -360,9 +377,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Name"); - b.HasIndex("OrderId"); - - b.ToTable("ServiceCosts"); + b.ToTable("Services"); }); modelBuilder.Entity("OTManager.Core.Entities.OT.Worker", b => @@ -380,7 +395,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("HourlyRate") .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") + .HasColumnType("decimal(2,2)") .HasDefaultValue(0m); b.Property("Name") @@ -417,12 +432,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("HourlyRate") .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") + .HasColumnType("decimal(2,2)") .HasDefaultValue(0m); b.Property("HoursWorked") .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") + .HasColumnType("decimal(2,2)") .HasDefaultValue(0m); b.Property("Name") @@ -434,7 +449,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("TotalCost") .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") + .HasColumnType("decimal(2,2)") .HasDefaultValue(0m); b.Property("UpdatedAt") diff --git a/OTManager.Data/Repositories/Implements/ClientRepository.cs b/OTManager.Data/Repositories/Implements/ClientRepository.cs index 045ec6b..de479ee 100644 --- a/OTManager.Data/Repositories/Implements/ClientRepository.cs +++ b/OTManager.Data/Repositories/Implements/ClientRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/FactureRepository.cs b/OTManager.Data/Repositories/Implements/FactureRepository.cs index c0303a4..e26e066 100644 --- a/OTManager.Data/Repositories/Implements/FactureRepository.cs +++ b/OTManager.Data/Repositories/Implements/FactureRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/MaterialCostRepository.cs b/OTManager.Data/Repositories/Implements/MaterialCostRepository.cs index ca70fd4..ac8c779 100644 --- a/OTManager.Data/Repositories/Implements/MaterialCostRepository.cs +++ b/OTManager.Data/Repositories/Implements/MaterialCostRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/MaterialRepository.cs b/OTManager.Data/Repositories/Implements/MaterialRepository.cs index 728059c..3d70a3d 100644 --- a/OTManager.Data/Repositories/Implements/MaterialRepository.cs +++ b/OTManager.Data/Repositories/Implements/MaterialRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/OrderRepository.cs b/OTManager.Data/Repositories/Implements/OrderRepository.cs index c9d21e4..01a4ee1 100644 --- a/OTManager.Data/Repositories/Implements/OrderRepository.cs +++ b/OTManager.Data/Repositories/Implements/OrderRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/OrderServiceRepository.cs b/OTManager.Data/Repositories/Implements/OrderServiceRepository.cs index 57ae18c..0b40f61 100644 --- a/OTManager.Data/Repositories/Implements/OrderServiceRepository.cs +++ b/OTManager.Data/Repositories/Implements/OrderServiceRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/ServiceCostRepository.cs b/OTManager.Data/Repositories/Implements/ServiceCostRepository.cs index f2d8042..3029c87 100644 --- a/OTManager.Data/Repositories/Implements/ServiceCostRepository.cs +++ b/OTManager.Data/Repositories/Implements/ServiceCostRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/WorkerCostRepository.cs b/OTManager.Data/Repositories/Implements/WorkerCostRepository.cs index da115a9..a7e2f42 100644 --- a/OTManager.Data/Repositories/Implements/WorkerCostRepository.cs +++ b/OTManager.Data/Repositories/Implements/WorkerCostRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/Implements/WorkerRepository.cs b/OTManager.Data/Repositories/Implements/WorkerRepository.cs index dd33a5c..7ce9ffb 100644 --- a/OTManager.Data/Repositories/Implements/WorkerRepository.cs +++ b/OTManager.Data/Repositories/Implements/WorkerRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; + using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.Context; diff --git a/OTManager.Data/Repositories/RepositoryExtension.cs b/OTManager.Data/Repositories/RepositoryExtension.cs index b3813c3..5560866 100644 --- a/OTManager.Data/Repositories/RepositoryExtension.cs +++ b/OTManager.Data/Repositories/RepositoryExtension.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; + using OTManager.Data.Repositories.Implements; using OTManager.Data.Repositories.Interface; diff --git a/OTManager.Data/UoW/UnitOfWork.cs b/OTManager.Data/UoW/UnitOfWork.cs index deef99f..3c67465 100644 --- a/OTManager.Data/UoW/UnitOfWork.cs +++ b/OTManager.Data/UoW/UnitOfWork.cs @@ -1,10 +1,9 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Storage; + using OTManager.Data.Context; using OTManager.Data.Repositories.Implements; using OTManager.Data.Repositories.Interface; -using System; -using System.Threading.Tasks; namespace OTManager.Data.UoW { diff --git a/OTManager.Wapi.sln b/OTManager.Wapi.sln index e83de12..5f54849 100644 --- a/OTManager.Wapi.sln +++ b/OTManager.Wapi.sln @@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{C5CFB38B-0E9 README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OTManager.Web", "src\OTManager.Web\OTManager.Web.csproj", "{9F3A116A-1006-4770-81E4-D57CD3583D4C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +43,10 @@ Global {C450D449-89E9-43D4-A52D-A0F1DF8FE517}.Debug|Any CPU.Build.0 = Debug|Any CPU {C450D449-89E9-43D4-A52D-A0F1DF8FE517}.Release|Any CPU.ActiveCfg = Release|Any CPU {C450D449-89E9-43D4-A52D-A0F1DF8FE517}.Release|Any CPU.Build.0 = Release|Any CPU + {9F3A116A-1006-4770-81E4-D57CD3583D4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F3A116A-1006-4770-81E4-D57CD3583D4C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F3A116A-1006-4770-81E4-D57CD3583D4C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F3A116A-1006-4770-81E4-D57CD3583D4C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,6 +56,7 @@ Global {F113E42C-0222-4D95-9D54-4943F73716DA} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {4A32B4C2-DEF6-40BA-AE40-1DEF2D26D002} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {C450D449-89E9-43D4-A52D-A0F1DF8FE517} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {9F3A116A-1006-4770-81E4-D57CD3583D4C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8434D880-E4C5-4974-9EE1-67CD8E257B50} diff --git a/src/OTManager.Api/Controllers/MaterialController.cs b/src/OTManager.Api/Controllers/MaterialController.cs index 721a71a..830163e 100644 --- a/src/OTManager.Api/Controllers/MaterialController.cs +++ b/src/OTManager.Api/Controllers/MaterialController.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Mvc; -using OTManager.App.Services.Interfaces; + using OTManager.App.Dtos.Materials; +using OTManager.App.Services.Interfaces; +using OTManager.Core.QueryParams; namespace OTManager.Api.Controllers; @@ -16,10 +18,10 @@ public MaterialController(IMaterialService materialService) } [HttpGet] - public async Task GetAll() + public async Task GetAll([FromQuery] MaterialQueryParams query) { - var result = await _materialService.GetAllAsync(); - return Ok(result); + var (Items, TotalCount) = await _materialService.GetFilteredAsync(query); + return Ok(new { Items, TotalCount }); } [HttpGet("{id}")] diff --git a/src/OTManager.Api/Controllers/ServiceOrderController.cs b/src/OTManager.Api/Controllers/ServiceOrderController.cs index ea6b717..0e04376 100644 --- a/src/OTManager.Api/Controllers/ServiceOrderController.cs +++ b/src/OTManager.Api/Controllers/ServiceOrderController.cs @@ -1,6 +1,8 @@ using Microsoft.AspNetCore.Mvc; -using OTManager.App.Services.Interfaces; + using OTManager.App.Dtos.Orders; +using OTManager.App.Services.Interfaces; +using OTManager.Core.QueryParams; namespace OTManager.Api.Controllers; @@ -16,10 +18,10 @@ public ServiceOrderController(IOrderServiceAppService serviceOrderService) } [HttpGet] - public async Task GetAll() + public async Task GetAll([FromQuery] OrderServiceQueryParams query) { - var result = await _serviceOrderService.GetAllAsync(); - return Ok(result); + var (Items, TotalCount) = await _serviceOrderService.GetFilteredAsync(query); + return Ok(new { Items, TotalCount }); } [HttpGet("{id}")] diff --git a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/CreateOrderServiceEndpoint.cs b/src/OTManager.Api/Endpoints/OrderServiceEndpoints/CreateOrderServiceEndpoint.cs deleted file mode 100644 index f0dfcca..0000000 --- a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/CreateOrderServiceEndpoint.cs +++ /dev/null @@ -1,30 +0,0 @@ -using FastEndpoints; -using OTManager.App.Dtos.Orders; -using OTManager.App.Services.Interfaces; - -namespace OTManager.Api.Endpoints.OrderServiceEndpoints; - -public class CreateOrderServiceEndpoint : Endpoint -{ - private readonly IOrderServiceAppService _service; - public CreateOrderServiceEndpoint(IOrderServiceAppService service) => _service = service; - - public override void Configure() - { - Post("/orderservices"); - AllowAnonymous(); - Summary(s => - { - s.Summary = "Crea un nuevo servicio de orden."; - }); - } - - public override async Task HandleAsync(ReadOrderServiceDto req, CancellationToken ct) - { - if (string.IsNullOrWhiteSpace(req.Name)) AddError(r => r.Name, "El nombre es obligatorio."); - if (req.Price < 0) AddError(r => r.Price, "El precio debe ser mayor o igual a 0."); - if (ValidationFailed) return; - var created = await _service.CreateAsync(req); - await HttpContext.Response.SendAsync(created, 201, null, ct); - } -} diff --git a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/DeleteOrderServiceEndpoint.cs b/src/OTManager.Api/Endpoints/OrderServiceEndpoints/DeleteOrderServiceEndpoint.cs deleted file mode 100644 index 42433d6..0000000 --- a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/DeleteOrderServiceEndpoint.cs +++ /dev/null @@ -1,39 +0,0 @@ -using FastEndpoints; -using OTManager.App.Dtos.Orders; -using OTManager.App.Services.Interfaces; - -namespace OTManager.Api.Endpoints.OrderServiceEndpoints; - -public class DeleteOrderServiceEndpoint : EndpointWithoutRequest -{ - private readonly IOrderServiceAppService _service; - public DeleteOrderServiceEndpoint(IOrderServiceAppService service) => _service = service; - - public override void Configure() - { - Delete("/orderservices/{id:guid}"); - AllowAnonymous(); - Summary(s => - { - s.Summary = "Elimina un servicio de orden por su identificador."; - }); - } - - public override async Task HandleAsync(CancellationToken ct) - { - var id = Route("id"); - if (id == Guid.Empty) - { - AddError("id", "El id proporcionado no es vlido."); - await HttpContext.Response.SendErrorsAsync(ValidationFailures, 400, null, ct); - return; - } - var deleted = await _service.DeleteAsync(id); - if (!deleted) - { - await HttpContext.Response.SendAsync(null, 404, null, ct); - return; - } - await HttpContext.Response.SendAsync(new { deleted }, 200, null, ct); - } -} diff --git a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceByIdEndpoint.cs b/src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceByIdEndpoint.cs deleted file mode 100644 index 487fad3..0000000 --- a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceByIdEndpoint.cs +++ /dev/null @@ -1,39 +0,0 @@ -using FastEndpoints; -using OTManager.App.Dtos.Orders; -using OTManager.App.Services.Interfaces; - -namespace OTManager.Api.Endpoints.OrderServiceEndpoints; - -public class GetOrderServiceByIdEndpoint : EndpointWithoutRequest -{ - private readonly IOrderServiceAppService _service; - public GetOrderServiceByIdEndpoint(IOrderServiceAppService service) => _service = service; - - public override void Configure() - { - Get("/orderservices/{id:guid}"); - AllowAnonymous(); - Summary(s => - { - s.Summary = "Obtiene un servicio de orden por Id."; - }); - } - - public override async Task HandleAsync(CancellationToken ct) - { - var id = Route("id"); - if (id == Guid.Empty) - { - AddError("id", "El id proporcionado no es vlido."); - await HttpContext.Response.SendErrorsAsync(ValidationFailures, 400, null, ct); - return; - } - var result = await _service.GetByIdAsync(id); - if (result is null) - { - await HttpContext.Response.SendAsync(null, 404, null, ct); - return; - } - await HttpContext.Response.SendAsync(result, 200, null, ct); - } -} diff --git a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceEndpoint.cs b/src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceEndpoint.cs deleted file mode 100644 index 30c6bd9..0000000 --- a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/GetOrderServiceEndpoint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FastEndpoints; -using OTManager.App.Dtos.Orders; -using OTManager.App.Services.Interfaces; - -namespace OTManager.Api.Endpoints.OrderServiceEndpoints; - -public class GetOrderServiceEndpoint : EndpointWithoutRequest -{ - private readonly IOrderServiceAppService _service; - public GetOrderServiceEndpoint(IOrderServiceAppService service) => _service = service; - - public override void Configure() - { - Get("/orderservices"); - AllowAnonymous(); - Summary(s => - { - s.Summary = "Obtiene todos los servicios de orden."; - }); - } - - public override async Task HandleAsync(CancellationToken ct) - { - var result = await _service.GetAllAsync(); - await HttpContext.Response.SendAsync(result, 200, null, ct); - } -} diff --git a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/UpdateOrderServiceEndpoint.cs b/src/OTManager.Api/Endpoints/OrderServiceEndpoints/UpdateOrderServiceEndpoint.cs deleted file mode 100644 index 3506ce3..0000000 --- a/src/OTManager.Api/Endpoints/OrderServiceEndpoints/UpdateOrderServiceEndpoint.cs +++ /dev/null @@ -1,42 +0,0 @@ -using FastEndpoints; -using OTManager.App.Dtos.Orders; -using OTManager.App.Services.Interfaces; - -namespace OTManager.Api.Endpoints.OrderServiceEndpoints; - -public class UpdateOrderServiceEndpoint : Endpoint -{ - private readonly IOrderServiceAppService _service; - public UpdateOrderServiceEndpoint(IOrderServiceAppService service) => _service = service; - - public override void Configure() - { - Put("/orderservices/{id:guid}"); - AllowAnonymous(); - Summary(s => - { - s.Summary = "Actualiza un servicio de orden existente."; - }); - } - - public override async Task HandleAsync(ReadOrderServiceDto req, CancellationToken ct) - { - var id = Route("id"); - if (id == Guid.Empty) - { - AddError("id", "El id proporcionado no es vlido."); - await HttpContext.Response.SendErrorsAsync(ValidationFailures, 400, null, ct); - return; - } - if (string.IsNullOrWhiteSpace(req.Name)) AddError(r => r.Name, "El nombre es obligatorio."); - if (req.Price < 0) AddError(r => r.Price, "El precio debe ser mayor o igual a 0."); - if (ValidationFailed) return; - var updated = await _service.UpdateAsync(id, req); - if (!updated) - { - await HttpContext.Response.SendAsync(null, 404, null, ct); - return; - } - await HttpContext.Response.SendAsync(new { updated }, 200, null, ct); - } -} diff --git a/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs b/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs index 6c422b3..d02f5a7 100644 --- a/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs +++ b/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs @@ -1,9 +1,8 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Configuration; +using OTManager.App.Mappers; +using OTManager.App.Services; using OTManager.Data.Context; using OTManager.Data.Repositories; using OTManager.Data.UoW; -using OTManager.App.Services; namespace OTManager.Api.Extensions; @@ -12,9 +11,12 @@ public static class ApplicationDependencyInjection public static IServiceCollection AddApplicationDependency(this IServiceCollection services, IConfiguration configuration) { services.AddDbContextExtension(configuration); + services.AddSwaggerExtension(configuration); services.AddRepositoryExtension(); services.AddUnitOfWorkExtension(); - services.AddAppServices(); // <-- Registro de servicios de aplicacin + services.AddControllers(); + services.AddAppServices(); + services.AddAppMappers(); return services; } } diff --git a/src/OTManager.Api/Extensions/DependencyInjection.cs b/src/OTManager.Api/Extensions/DependencyInjection.cs deleted file mode 100644 index 464c7ae..0000000 --- a/src/OTManager.Api/Extensions/DependencyInjection.cs +++ /dev/null @@ -1,24 +0,0 @@ -using OTManager.Data.Context; -using OTManager.Data.Repositories; -using OTManager.Data.UoW; - -namespace OTManager.Api.Extensions; - -public static class DependencyInjection -{ - public static IServiceCollection AddApplicationDependency(this IServiceCollection services, IConfiguration configuration) - { - // ToDo: Add services here - services.AddControllers(); - //services.AddMapperExtension(); - services.AddSwaggerExtension(configuration); - services.AddDbContextExtension(configuration); - - services.AddScoped(); - - services.AddRepositoryExtension(); - //services.AddServicesExtension(); - - return services; - } -} diff --git a/src/OTManager.Api/OTManager.Api.csproj b/src/OTManager.Api/OTManager.Api.csproj index 258feb9..0932c0d 100644 --- a/src/OTManager.Api/OTManager.Api.csproj +++ b/src/OTManager.Api/OTManager.Api.csproj @@ -8,7 +8,6 @@ - all diff --git a/src/OTManager.Api/Program.cs b/src/OTManager.Api/Program.cs index b828342..899d14f 100644 --- a/src/OTManager.Api/Program.cs +++ b/src/OTManager.Api/Program.cs @@ -1,11 +1,9 @@ using OTManager.Api.Extensions; -using FastEndpoints; var builder = WebApplication.CreateBuilder(args); // Usar el nombre de clase esttico para evitar ambigedad ApplicationDependencyInjection.AddApplicationDependency(builder.Services, builder.Configuration); -builder.Services.AddFastEndpoints(); var app = builder.Build(); @@ -19,6 +17,6 @@ app.UseHttpsRedirection(); -app.UseFastEndpoints(); +app.MapControllers(); app.Run(); diff --git a/src/OTManager.App/Dtos/Clients/ReadClientDto.cs b/src/OTManager.App/Dtos/Clients/ReadClientDto.cs index 72b2f29..77e268e 100644 --- a/src/OTManager.App/Dtos/Clients/ReadClientDto.cs +++ b/src/OTManager.App/Dtos/Clients/ReadClientDto.cs @@ -1,6 +1,5 @@ using OTManager.App.Dtos.Factures; using OTManager.App.Dtos.Orders; -using System.Collections.Generic; namespace OTManager.App.Dtos.Clients; diff --git a/src/OTManager.App/Mappers/MapperExtension.cs b/src/OTManager.App/Mappers/MapperExtension.cs new file mode 100644 index 0000000..2b5ef42 --- /dev/null +++ b/src/OTManager.App/Mappers/MapperExtension.cs @@ -0,0 +1,23 @@ +using Microsoft.Extensions.DependencyInjection; + +using OTManager.App.Mappers.Implements; +using OTManager.App.Mappers.Interfaces; + +namespace OTManager.App.Mappers; + +public static class MapperExtension +{ + public static IServiceCollection AddAppMappers(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } +} diff --git a/src/OTManager.App/Services/Implements/OrderServiceService.cs b/src/OTManager.App/Services/Implements/OrderServiceService.cs index 76090e0..b3d2490 100644 --- a/src/OTManager.App/Services/Implements/OrderServiceService.cs +++ b/src/OTManager.App/Services/Implements/OrderServiceService.cs @@ -1,8 +1,8 @@ using OTManager.App.Dtos.Orders; using OTManager.App.Services.Interfaces; +using OTManager.Core.Entities.OT; using OTManager.Core.QueryParams; using OTManager.Data.UoW; -using OTManager.Core.Entities.OT; namespace OTManager.App.Services.Implements; diff --git a/src/OTManager.App/Services/Interfaces/IOrderServiceService.cs b/src/OTManager.App/Services/Interfaces/IOrderServiceAppService.cs similarity index 100% rename from src/OTManager.App/Services/Interfaces/IOrderServiceService.cs rename to src/OTManager.App/Services/Interfaces/IOrderServiceAppService.cs diff --git a/src/OTManager.App/Services/ServiceExtension.cs b/src/OTManager.App/Services/ServiceExtension.cs index f1e7ded..e81efb9 100644 --- a/src/OTManager.App/Services/ServiceExtension.cs +++ b/src/OTManager.App/Services/ServiceExtension.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; -using OTManager.App.Services.Interfaces; + using OTManager.App.Services.Implements; +using OTManager.App.Services.Interfaces; namespace OTManager.App.Services; @@ -8,8 +9,16 @@ public static class ServiceExtension { public static IServiceCollection AddAppServices(this IServiceCollection services) { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); - // Agrega aqu otros servicios segn sea necesario + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + return services; } } diff --git a/src/OTManager.Core/Entities/OT/MaterialCost.cs b/src/OTManager.Core/Entities/OT/MaterialCost.cs index 789f515..dbdfba3 100644 --- a/src/OTManager.Core/Entities/OT/MaterialCost.cs +++ b/src/OTManager.Core/Entities/OT/MaterialCost.cs @@ -7,17 +7,17 @@ public class MaterialCost : Auditable public string Code { get; set; } = string.Empty!; public string Name { get; set; } = string.Empty!; public string MeasureUnit { get; set; } = string.Empty!; - public decimal UnitCost - { - get => _unitCost; - set { _unitCost = value; RecalculateTotalCost(); } + public decimal UnitCost + { + get => _unitCost; + set { _unitCost = value; RecalculateTotalCost(); } } public Guid OrderId { get; set; } public Order? Order { get; set; } - public decimal Quantity - { - get => _quantity; - set { _quantity = value; RecalculateTotalCost(); } + public decimal Quantity + { + get => _quantity; + set { _quantity = value; RecalculateTotalCost(); } } public decimal TotalCost { get; private set; } diff --git a/src/OTManager.Core/Entities/OT/ServiceCost.cs b/src/OTManager.Core/Entities/OT/ServiceCost.cs index 88003ea..f3f69ca 100644 --- a/src/OTManager.Core/Entities/OT/ServiceCost.cs +++ b/src/OTManager.Core/Entities/OT/ServiceCost.cs @@ -6,17 +6,17 @@ public class ServiceCost : Auditable { public string Name { get; set; } = string.Empty!; public string Description { get; set; } = string.Empty!; - public decimal Price - { - get => _price; - set { _price = value; RecalculateTotalPrice(); } + public decimal Price + { + get => _price; + set { _price = value; RecalculateTotalPrice(); } } public Guid OrderId { get; set; } public Order? Order { get; set; } - public int Quantity - { - get => _quantity; - set { _quantity = value; RecalculateTotalPrice(); } + public int Quantity + { + get => _quantity; + set { _quantity = value; RecalculateTotalPrice(); } } public decimal TotalPrice { get; private set; } diff --git a/src/OTManager.Core/Entities/OT/WorkerCost.cs b/src/OTManager.Core/Entities/OT/WorkerCost.cs index b18a38a..f7c663f 100644 --- a/src/OTManager.Core/Entities/OT/WorkerCost.cs +++ b/src/OTManager.Core/Entities/OT/WorkerCost.cs @@ -5,17 +5,17 @@ namespace OTManager.Core.Entities.OT; public class WorkerCost : Auditable { public string Name { get; set; } = string.Empty!; - public decimal HourlyRate - { - get => _hourlyRate; - set { _hourlyRate = value; RecalculateTotalCost(); } + public decimal HourlyRate + { + get => _hourlyRate; + set { _hourlyRate = value; RecalculateTotalCost(); } } public Guid OrderId { get; set; } public Order? Order { get; set; } - public decimal HoursWorked - { - get => _hoursWorked; - set { _hoursWorked = value; RecalculateTotalCost(); } + public decimal HoursWorked + { + get => _hoursWorked; + set { _hoursWorked = value; RecalculateTotalCost(); } } public decimal TotalCost { get; private set; } diff --git a/src/OTManager.Web/Components/App.razor b/src/OTManager.Web/Components/App.razor new file mode 100644 index 0000000..7774499 --- /dev/null +++ b/src/OTManager.Web/Components/App.razor @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor b/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor new file mode 100644 index 0000000..4c012b0 --- /dev/null +++ b/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor @@ -0,0 +1 @@ +

ClientListViewComponent

diff --git a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor new file mode 100644 index 0000000..ce2daeb --- /dev/null +++ b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor @@ -0,0 +1,5 @@ +@page "/Clients/Index" + +Clients | Index + + \ No newline at end of file diff --git a/src/OTManager.Web/Components/Clients/_Imports.razor b/src/OTManager.Web/Components/Clients/_Imports.razor new file mode 100644 index 0000000..2d3fc19 --- /dev/null +++ b/src/OTManager.Web/Components/Clients/_Imports.razor @@ -0,0 +1 @@ +@using OTManager.Web.Components.Clients.Components \ No newline at end of file diff --git a/src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor b/src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor new file mode 100644 index 0000000..a717b92 --- /dev/null +++ b/src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor @@ -0,0 +1,7 @@ +@page "/Home" + +

DashboardIndex

+ +@code { + +} diff --git a/src/OTManager.Web/Components/Layout/MainLayout.razor b/src/OTManager.Web/Components/Layout/MainLayout.razor new file mode 100644 index 0000000..9902076 --- /dev/null +++ b/src/OTManager.Web/Components/Layout/MainLayout.razor @@ -0,0 +1,33 @@ +@inherits LayoutComponentBase + + + + OTManager + + + + + + + + +
+ @Body +
+
+
+ + Documentation and demos + + About Blazor + +
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
diff --git a/src/OTManager.Web/Components/Layout/NavMenu.razor b/src/OTManager.Web/Components/Layout/NavMenu.razor new file mode 100644 index 0000000..557c070 --- /dev/null +++ b/src/OTManager.Web/Components/Layout/NavMenu.razor @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor b/src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor new file mode 100644 index 0000000..28ed318 --- /dev/null +++ b/src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor @@ -0,0 +1,41 @@ + + + + Icono + + + + @Content + + + @Username | @LastModification | @Status + + + + + + + + + + +@code { + [Parameter] + public string Content { get; set; } = string.Empty; + [Parameter] + public string Username { get; set; } = string.Empty; + [Parameter] + public string Status { get; set; } = string.Empty; + [Parameter] + public DateTime LastModification { get; set; } + [Parameter] + public EventCallback OnClickEvent { get; set; } +} \ No newline at end of file diff --git a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor new file mode 100644 index 0000000..3229842 --- /dev/null +++ b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor @@ -0,0 +1,44 @@ +@page "/Materials/Index" + +Materials | Index + + + + + Materials + + + + + + + + + + + + @if (Items is null) + { + + + + } + else + { + foreach (Item item in Items) + { + + + + + + } + } + diff --git a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs new file mode 100644 index 0000000..fef4282 --- /dev/null +++ b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs @@ -0,0 +1,29 @@ +namespace OTManager.Web.Components.Materials.Pages; + +public partial class StocksIndex +{ + private IQueryable? Items { get; set; } + + protected override async Task OnInitializedAsync() + { + await Task.Delay(500); + Items = GetDemoItems(); + } + + public static IQueryable GetDemoItems() => new List{ + new ("002", "Item 2", "U", 13.5m ), + new ("002", "Item 2", "U", 13.5m), + new ("002", "Item 2", "U", 13.5m), + new ("002", "Item 2", "U", 13.5m), + new ("002", "Item 2", "U", 13.5m), + new ("002", "Item 2", "U", 13.5m) + }.AsQueryable(); + + public record Item( + string Code, + string Name, + string MeasureUnit, + decimal UnitCost + ); +} + diff --git a/src/OTManager.Web/Components/Materials/_Imports.razor b/src/OTManager.Web/Components/Materials/_Imports.razor new file mode 100644 index 0000000..b45aa95 --- /dev/null +++ b/src/OTManager.Web/Components/Materials/_Imports.razor @@ -0,0 +1 @@ +@using OTManager.Web.Components.Materials.Components \ No newline at end of file diff --git a/src/OTManager.Web/Components/Pages/Error.razor b/src/OTManager.Web/Components/Pages/Error.razor new file mode 100644 index 0000000..576cc2d --- /dev/null +++ b/src/OTManager.Web/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

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

+

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

+ +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/src/OTManager.Web/Components/Pages/Home.razor b/src/OTManager.Web/Components/Pages/Home.razor new file mode 100644 index 0000000..96714a2 --- /dev/null +++ b/src/OTManager.Web/Components/Pages/Home.razor @@ -0,0 +1,7 @@ +@page "/" + +Home + +

Hello, world!

+ +Welcome to your new Fluent Blazor app. \ No newline at end of file diff --git a/src/OTManager.Web/Components/Routes.razor b/src/OTManager.Web/Components/Routes.razor new file mode 100644 index 0000000..f756e19 --- /dev/null +++ b/src/OTManager.Web/Components/Routes.razor @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/OTManager.Web/Components/Settings/Controllers/Localization/CultureController.cs b/src/OTManager.Web/Components/Settings/Controllers/Localization/CultureController.cs new file mode 100644 index 0000000..c53de3f --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Controllers/Localization/CultureController.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Localization; +using Microsoft.AspNetCore.Mvc; + +namespace OTManager.Web.Components.Settings.Controllers.Localization; + +[Route("[controller]/[action]")] +public class CultureController : Controller +{ + public IActionResult Set(string culture, string redirectUri) + { + if (culture != null) + { + var requestCulture = new RequestCulture(culture, culture); + var cookieName = CookieRequestCultureProvider.DefaultCookieName; + var cookieValue = CookieRequestCultureProvider.MakeCookieValue(requestCulture); + + HttpContext.Response.Cookies.Append(cookieName, cookieValue); + } + return LocalRedirect(redirectUri); + } +} diff --git a/src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor b/src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor new file mode 100644 index 0000000..5553b03 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor @@ -0,0 +1,48 @@ +@using OTManager.Web.Components.Layout + +@layout MainLayout +@inherits LayoutComponentBase + + + + Settings + + + + + + + + + + + + + + + + +
+ @Body +
+ + +@code { + +} diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingDataPage.razor b/src/OTManager.Web/Components/Settings/Pages/SettingDataPage.razor new file mode 100644 index 0000000..b67d8c4 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Pages/SettingDataPage.razor @@ -0,0 +1,107 @@ +@page "/settings/data" + +@layout SettingsLayout + + + + + Data Settings + + + Home + Settings + Data + + + + + + + + + Audith data settings + + + + + + + + + + + + + + + Audith data settings + + + + + + + + + + + + + + + records data settings + + + + + + + + + + + + + + + Db Context + + + + Status + Connected + + + Healthy + Good + + + + + + +@code{ + +} diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingLanguagePage.razor b/src/OTManager.Web/Components/Settings/Pages/SettingLanguagePage.razor new file mode 100644 index 0000000..9064962 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Pages/SettingLanguagePage.razor @@ -0,0 +1,85 @@ +@page "/settings/language" + +@layout SettingsLayout +@inject NavigationManager NavigationManager +@inject IStringLocalizer UiLang + + + + + @UiLang["SettingsLangTitle"] + + + @UiLang["NavHome"] + @UiLang["NavSettings"] + @UiLang["NavLanguage"] + + + + + + + + + + @UiLang["LangApiDescription"] + + + + + + + + +
+ +
+ @UiLang["LangUiDescription"] + @localizer.GetValueOrDefault() +
+
+ +
+ +@code { + public LocalizerSelect? localizer { get; set; } + + protected override void OnInitialized() + { + Culture = CultureInfo.CurrentCulture; + } + + private CultureInfo Culture + { + get { return CultureInfo.CurrentCulture; } + set + { + if (CultureInfo.CurrentCulture != value) + { + var uri = new Uri(NavigationManager.Uri) + .GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped); + var cultureEscaped = Uri.EscapeDataString(value.Name); + var uriEscaped = Uri.EscapeDataString(uri); + + NavigationManager.NavigateTo($"Culture/Set?culture={cultureEscaped}&redirectUri={uriEscaped}", forceLoad: true); + } + } + } +} \ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingThemePage.razor b/src/OTManager.Web/Components/Settings/Pages/SettingThemePage.razor new file mode 100644 index 0000000..1255176 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Pages/SettingThemePage.razor @@ -0,0 +1,82 @@ +@page "/settings/theme" + +@layout SettingsLayout + + + + Theme Settings + + + Home + Settings + Theme + + + + + + + + + + Selected the theme in 3 options + System: follow the system theme + Light: a light theme + Dark: a dark theme + + + + + + + + + + @context + + + + Select the options of color from your preference + + + + + + Get random style + Feeling lucky? + + + + + + +@code { + public DesignThemeModes Mode { get; set; } + + public OfficeColor? OfficeColor { get; set; } + + void PickRandomColor() + { + OfficeColor = OfficeColorUtilities.GetRandom(); + } +} \ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor new file mode 100644 index 0000000..36cdb49 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor @@ -0,0 +1,40 @@ +@page "/settings/users" + +@layout SettingsLayout + + + + Users Settings + +
+ Add + Export + Print +
+ + + Home + Settings + Users + +
+ + + @if (users == null) + { +

Loading...

+ } + else + { + @* + + + + *@ + } +
\ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs new file mode 100644 index 0000000..295e5fd --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs @@ -0,0 +1,20 @@ +namespace OTManager.Web.Components.Settings.Pages; + +public partial class SettingUserPage +{ + public IQueryable? users; + + protected override async Task OnInitializedAsync() + { + // Simulate asynchronous loading to demonstrate a loading indicator + await Task.Delay(500); + GetAll(); + + } + + private void GetAll() + { + //var response = await _user.Users.ToListAsync(); + //if (response is not null) users = response.AsQueryable(); + } +} diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingsPage.razor b/src/OTManager.Web/Components/Settings/Pages/SettingsPage.razor new file mode 100644 index 0000000..ca83c26 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Pages/SettingsPage.razor @@ -0,0 +1,33 @@ +@page "/Settings" + + +@layout SettingsLayout + + + + Overview + + + Home + Settings + + + + + + Some fancy text + + + + +@code { + +} diff --git a/src/OTManager.Web/Components/Settings/Pages/_Imports.razor b/src/OTManager.Web/Components/Settings/Pages/_Imports.razor new file mode 100644 index 0000000..00ae358 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Pages/_Imports.razor @@ -0,0 +1,6 @@ +@using System.Globalization +@using Microsoft.Extensions.Localization +@using Microsoft.FluentUI.AspNetCore.Components.Extensions +@using OTManager.Web.Components.Settings.Layout +@using OTManager.Web.Components.Settings.Resources.Enums +@using OTManager.Web.Components.Settings.Resources.Localization diff --git a/src/OTManager.Web/Components/Settings/Resources/Enums/LocalizerSelect.cs b/src/OTManager.Web/Components/Settings/Resources/Enums/LocalizerSelect.cs new file mode 100644 index 0000000..024cd8f --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Resources/Enums/LocalizerSelect.cs @@ -0,0 +1,11 @@ +using System.ComponentModel; + +namespace OTManager.Web.Components.Settings.Resources.Enums; + +public enum LocalizerSelect +{ + [Description("es-ES")] + Español, + [Description("en-EU")] + English +} diff --git a/src/OTManager.Web/Components/Settings/Resources/Enums/StyleColor.cs b/src/OTManager.Web/Components/Settings/Resources/Enums/StyleColor.cs new file mode 100644 index 0000000..54e74ae --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Resources/Enums/StyleColor.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace OTManager.Web.Components.Settings.Resources.Enums; + +public enum StyleColor +{ + // + // Resumen: + // The default Fluent UI accent color + [Description("default")] + Default, + [Description("#a4373a")] + Access, + [Description("#0078d4")] + Exchange, + [Description("#217346")] + Excel, + [Description("#d83b01")] + Office +} diff --git a/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.Designer.cs b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.Designer.cs new file mode 100644 index 0000000..1d94cfd --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.Designer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// +// Este código fue generado por una herramienta. +// Versión de runtime:4.0.30319.42000 +// +// Los cambios en este archivo podrían causar un comportamiento incorrecto y se perderán si +// se vuelve a generar el código. +// +//------------------------------------------------------------------------------ + +namespace OTManager.Web.Components.Settings.Resources.Localization { + using System; + + + /// + /// Clase de recurso fuertemente tipado, para buscar cadenas traducidas, etc. + /// + // StronglyTypedResourceBuilder generó automáticamente esta clase + // a través de una herramienta como ResGen o Visual Studio. + // Para agregar o quitar un miembro, edite el archivo .ResX y, a continuación, vuelva a ejecutar ResGen + // con la opción /str o recompile su proyecto de VS. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class UiLanguage { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal UiLanguage() { + } + + /// + /// Devuelve la instancia de ResourceManager almacenada en caché utilizada por esta clase. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OTManager.Web.Components.Settings.Resources.Localization.UiLanguage", typeof(UiLanguage).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Reemplaza la propiedad CurrentUICulture del subproceso actual para todas las + /// búsquedas de recursos mediante esta clase de recurso fuertemente tipado. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Busca una cadena traducida similar a Manage the language for the notifications ands responses from the server default [en-EU]. + /// + public static string LangApiDescription { + get { + return ResourceManager.GetString("LangApiDescription", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Manage the language for the UI applications and is manage from the user preference . + /// + public static string LangUiDescription { + get { + return ResourceManager.GetString("LangUiDescription", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Home. + /// + public static string NavHome { + get { + return ResourceManager.GetString("NavHome", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Language. + /// + public static string NavLanguage { + get { + return ResourceManager.GetString("NavLanguage", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Settings. + /// + public static string NavSettings { + get { + return ResourceManager.GetString("NavSettings", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Pong. + /// + public static string ping { + get { + return ResourceManager.GetString("ping", resourceCulture); + } + } + + /// + /// Busca una cadena traducida similar a Language. + /// + public static string SettingsLangTitle { + get { + return ResourceManager.GetString("SettingsLangTitle", resourceCulture); + } + } + } +} diff --git a/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.en-US.resx b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.en-US.resx new file mode 100644 index 0000000..4464322 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.en-US.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Manage the language for the notifications ands responses from the server, default [en-EU]. + + + Manage the language for the UI applications and is manage from the user preference. + + + Home + + + Language + + + Settings + + + Pong en-US + + + Language Settings + + \ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.es-ES.resx b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.es-ES.resx new file mode 100644 index 0000000..0590ac5 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.es-ES.resx @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Gestiona el idioma de las notificaciones y respuestas desde el servidor, por defecto [en-EU]. + + + Gestiona el idioma de la interface según las preferencias de usuario. + + + Inicio + + + Idioma + + + Configuración + + + Pong es-ES + + + Configuración de idioma + + \ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.resx b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.resx new file mode 100644 index 0000000..5c636c9 --- /dev/null +++ b/src/OTManager.Web/Components/Settings/Resources/Localization/UiLanguage.resx @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Manage the language for the notifications ands responses from the server default [en-EU] + + + Manage the language for the UI applications and is manage from the user preference + + + Home + + + Language + + + Settings + + + Pong + Test resource string + + + Language + Settings Language Title + + \ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/_Imports.razor b/src/OTManager.Web/Components/Settings/_Imports.razor new file mode 100644 index 0000000..e69de29 diff --git a/src/OTManager.Web/Components/_Imports.razor b/src/OTManager.Web/Components/_Imports.razor new file mode 100644 index 0000000..d8c46e1 --- /dev/null +++ b/src/OTManager.Web/Components/_Imports.razor @@ -0,0 +1,12 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.JSInterop +@using OTManager.Web +@using OTManager.Web.Components diff --git a/src/OTManager.Web/OTManager.Web.csproj b/src/OTManager.Web/OTManager.Web.csproj new file mode 100644 index 0000000..8e5dfe2 --- /dev/null +++ b/src/OTManager.Web/OTManager.Web.csproj @@ -0,0 +1,13 @@ + + + + net9.0 + enable + enable + + + + + + + diff --git a/src/OTManager.Web/Program.cs b/src/OTManager.Web/Program.cs new file mode 100644 index 0000000..94dc547 --- /dev/null +++ b/src/OTManager.Web/Program.cs @@ -0,0 +1,44 @@ +using Microsoft.FluentUI.AspNetCore.Components; + +using OTManager.Web.Components; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents(); +builder.Services.AddFluentUIComponents(); + +builder.Services.AddLocalization(); +builder.Services.AddControllers(); + +var app = builder.Build(); + +string[] supportedCultures = ["es-ES", "en-US"]; + +var localizationOptions = new RequestLocalizationOptions() + .SetDefaultCulture(supportedCultures[0]) + .AddSupportedCultures(supportedCultures) + .AddSupportedUICultures(supportedCultures); + +app.UseRequestLocalization(localizationOptions); + +// Configure the HTTP request pipeline. +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +app.UseHttpsRedirection(); + +app.UseAntiforgery(); + +app.MapStaticAssets(); +app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + +app.MapControllers(); + +app.Run(); diff --git a/src/OTManager.Web/Properties/launchSettings.json b/src/OTManager.Web/Properties/launchSettings.json new file mode 100644 index 0000000..ae7aa43 --- /dev/null +++ b/src/OTManager.Web/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5200", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7230;http://localhost:5200", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } + } diff --git a/src/OTManager.Web/appsettings.Development.json b/src/OTManager.Web/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/OTManager.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/OTManager.Web/appsettings.json b/src/OTManager.Web/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/src/OTManager.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/OTManager.Web/wwwroot/app.css b/src/OTManager.Web/wwwroot/app.css new file mode 100644 index 0000000..77cf978 --- /dev/null +++ b/src/OTManager.Web/wwwroot/app.css @@ -0,0 +1,191 @@ +@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css'; + +body { + --body-font: "Segoe UI Variable", "Segoe UI", sans-serif; + font-family: var(--body-font); + font-size: var(--type-ramp-base-font-size); + line-height: var(--type-ramp-base-line-height); + margin: 0; +} + +.navmenu-icon { + display: none; +} + +.main { + min-height: calc(100dvh - 86px); + color: var(--neutral-foreground-rest); + align-items: stretch !important; +} + +.body-content { + align-self: stretch; + height: calc(100dvh - 86px) !important; + display: flex; +} + +.content { + padding: 0.5rem 1.5rem; + align-self: stretch !important; + width: 100%; +} + +.manage { + width: 100dvw; +} + +footer { + background: var(--neutral-layer-4); + color: var(--neutral-foreground-rest); + align-items: center; + padding: 10px 10px; +} + + footer a { + color: var(--neutral-foreground-rest); + text-decoration: none; + } + + footer a:focus { + outline: 1px dashed; + outline-offset: 3px; + } + + footer a:hover { + text-decoration: underline; + } + +.alert { + border: 1px dashed var(--accent-fill-rest); + padding: 5px; +} + + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; + margin: 20px 0; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::before { + content: "An error has occurred. " + } + +.loading-progress { + position: relative; + display: block; + width: 8rem; + height: 8rem; + margin: 20vh auto 1rem auto; +} + + .loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + +.loading-progress-text { + position: absolute; + text-align: center; + font-weight: bold; + inset: calc(20vh + 3.25rem) 0 auto 0.2rem; +} + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } + +code { + color: #c02d76; +} + +@media (max-width: 600px) { + .header-gutters { + margin: 0.5rem 3rem 0.5rem 1.5rem !important; + } + + [dir="rtl"] .header-gutters { + margin: 0.5rem 1.5rem 0.5rem 3rem !important; + } + + .main { + flex-direction: column !important; + row-gap: 0 !important; + } + + nav.sitenav { + width: 100%; + height: 100%; + } + + #main-menu { + width: 100% !important; + } + + #main-menu > div:first-child:is(.expander) { + display: none; + } + + .navmenu { + width: 100%; + } + + #navmenu-toggle { + appearance: none; + } + + #navmenu-toggle ~ nav { + display: none; + } + + #navmenu-toggle:checked ~ nav { + display: block; + } + + .navmenu-icon { + cursor: pointer; + z-index: 10; + display: block; + position: absolute; + top: 15px; + left: unset; + right: 20px; + width: 20px; + height: 20px; + border: none; + } + + [dir="rtl"] .navmenu-icon { + left: 20px; + right: unset; + } +} From d94cd5346ec88a34319fb946a577f3c5b77f6cf8 Mon Sep 17 00:00:00 2001 From: Adrian Mesa Sacasas Date: Sat, 25 Oct 2025 13:06:16 -0400 Subject: [PATCH 2/4] feat: trabajos en el sistema de authenticacion --- OTManager.Data/Account/ApplicationRole.cs | 13 + OTManager.Data/Account/ApplicationUser.cs | 8 + .../Context/ApplicationDbContext.cs | 23 +- .../20251009232432_InitialCommit.Designer.cs | 541 ------------ .../20251010212249_Models&ConfigsUpdates.cs | 777 ------------------ ...15220442_AuthenticationCommit.Designer.cs} | 176 +++- ...=> 20251015220442_AuthenticationCommit.cs} | 346 +++++--- .../ApplicationDbContextModelSnapshot.cs | 172 +++- OTManager.Data/OTManager.Data.csproj | 12 +- .../Implements/MaterialCostRepository.cs | 57 -- .../Implements/ServiceCostRepository.cs | 55 -- .../Implements/WorkerCostRepository.cs | 55 -- .../Interface/IMaterialCostRepository.cs | 10 - .../Interface/IServiceCostRepository.cs | 10 - .../Interface/IWorkerCostRepository.cs | 10 - .../Repositories/RepositoryExtension.cs | 3 - OTManager.Data/UoW/IUnitOfWork.cs | 3 - OTManager.Data/UoW/UnitOfWork.cs | 6 - .../Account/Controllers/SessionController.cs | 93 +++ .../Account/Controllers/UserController.cs | 15 + .../Account/Dto/LoginRequestDto.cs | 3 + .../Account/Extensions/IdentityExtension.cs | 73 ++ .../Account/Records/SessionInfo.cs | 5 + .../Account/Records/SessionResponse.cs | 17 + .../Account/Records/TokenResponse.cs | 4 + .../Services/Implement/JwtTokenService.cs | 76 ++ .../Services/Implement/SessionContext.cs | 44 + .../Services/Interface/IJwtTokenService.cs | 9 + .../Services/Interface/ISessionContext.cs | 9 + .../Controllers/MaterialController.cs | 2 + .../Controllers/ServiceOrderController.cs | 2 + .../ApplicationDependencyInjection.cs | 38 + src/OTManager.Api/OTManager.Api.csproj | 5 +- src/OTManager.Api/Program.cs | 4 + .../Implements/MaterialCostService.cs | 102 --- .../Services/Implements/ServiceCostService.cs | 102 --- .../Services/Implements/WorkerCostService.cs | 102 --- .../Interfaces/IMaterialCostService.cs | 14 - .../Interfaces/IServiceCostService.cs | 14 - .../Services/Interfaces/IWorkerCostService.cs | 14 - .../Services/ServiceExtension.cs | 3 - .../Entities/Abstracts/Auditable.cs | 4 +- .../Entities/Abstracts/IEntity.cs | 4 +- .../Authorize/AccountManagement.cs | 8 + .../ApiAuthenticationStateProvider.cs | 72 ++ .../AuthenticationService/AuthService.cs | 118 +++ .../AuthorizationMessageHandler.cs | 28 + .../CookieAuthTokenProvider.cs | 64 ++ .../AuthenticationService/IAuthService.cs | 11 + .../IAuthStateNotifier.cs | 8 + .../IAuthTokenProvider.cs | 8 + .../JSAuthTokenProvider.cs | 46 ++ .../ClientServices/Authorize/UserService.cs | 14 + .../DTOs/Enterprises/EnterpriseReadDto.cs | 20 + .../DTOs/Identity/JwtSettings.cs | 6 + .../DTOs/Identity/LoginRequest.cs | 4 + .../DTOs/Identity/LoginResponse.cs | 9 + .../DTOs/Identity/RegisterRequest.cs | 4 + .../DTOs/Identity/UserReadDto.cs | 9 + .../Account/Pages/AccountManagementPage.razor | 5 + .../Components/Account/Pages/Auth/Login.razor | 36 + .../Account/Pages/Auth/Register.razor | 29 + .../Account/Pages/User/Profile.razor | 37 + src/OTManager.Web/Components/App.razor | 2 +- .../Components/ClientListViewComponent.razor | 54 +- .../Components/ContextMenuComponent.razor | 46 ++ .../Components/OverseeMenuComponent.razor | 8 + .../Clients/Pages/ClientsIndex.razor | 93 ++- .../Clients/Pages/ClientsIndex.razor.cs | 75 ++ .../Dashboards/Pages/DashboardIndex.razor | 54 +- .../Components/Layout/MainLayout.razor | 9 + .../Materials/Pages/StocksIndex.razor.cs | 12 +- src/OTManager.Web/Components/Pages/Home.razor | 22 +- src/OTManager.Web/Components/Routes.razor | 21 +- .../Settings/Pages/SettingUserPage.razor | 2 + .../Settings/Pages/SettingUserPage.razor.cs | 23 +- .../ApplicationDependencyInjection.cs | 34 + src/OTManager.Web/OTManager.Web.csproj | 5 +- src/OTManager.Web/Program.cs | 50 +- src/OTManager.Web/wwwroot/Docs/LICENSE.txt | 21 + src/OTManager.Web/wwwroot/Docs/README.md | 40 + 81 files changed, 2097 insertions(+), 2045 deletions(-) create mode 100644 OTManager.Data/Account/ApplicationRole.cs create mode 100644 OTManager.Data/Account/ApplicationUser.cs delete mode 100644 OTManager.Data/Migrations/20251009232432_InitialCommit.Designer.cs delete mode 100644 OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs rename OTManager.Data/Migrations/{20251010212249_Models&ConfigsUpdates.Designer.cs => 20251015220442_AuthenticationCommit.Designer.cs} (75%) rename OTManager.Data/Migrations/{20251009232432_InitialCommit.cs => 20251015220442_AuthenticationCommit.cs} (55%) delete mode 100644 OTManager.Data/Repositories/Implements/MaterialCostRepository.cs delete mode 100644 OTManager.Data/Repositories/Implements/ServiceCostRepository.cs delete mode 100644 OTManager.Data/Repositories/Implements/WorkerCostRepository.cs delete mode 100644 OTManager.Data/Repositories/Interface/IMaterialCostRepository.cs delete mode 100644 OTManager.Data/Repositories/Interface/IServiceCostRepository.cs delete mode 100644 OTManager.Data/Repositories/Interface/IWorkerCostRepository.cs create mode 100644 src/OTManager.Api/Account/Controllers/SessionController.cs create mode 100644 src/OTManager.Api/Account/Controllers/UserController.cs create mode 100644 src/OTManager.Api/Account/Dto/LoginRequestDto.cs create mode 100644 src/OTManager.Api/Account/Extensions/IdentityExtension.cs create mode 100644 src/OTManager.Api/Account/Records/SessionInfo.cs create mode 100644 src/OTManager.Api/Account/Records/SessionResponse.cs create mode 100644 src/OTManager.Api/Account/Records/TokenResponse.cs create mode 100644 src/OTManager.Api/Account/Services/Implement/JwtTokenService.cs create mode 100644 src/OTManager.Api/Account/Services/Implement/SessionContext.cs create mode 100644 src/OTManager.Api/Account/Services/Interface/IJwtTokenService.cs create mode 100644 src/OTManager.Api/Account/Services/Interface/ISessionContext.cs delete mode 100644 src/OTManager.App/Services/Implements/MaterialCostService.cs delete mode 100644 src/OTManager.App/Services/Implements/ServiceCostService.cs delete mode 100644 src/OTManager.App/Services/Implements/WorkerCostService.cs delete mode 100644 src/OTManager.App/Services/Interfaces/IMaterialCostService.cs delete mode 100644 src/OTManager.App/Services/Interfaces/IServiceCostService.cs delete mode 100644 src/OTManager.App/Services/Interfaces/IWorkerCostService.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs create mode 100644 src/OTManager.Web/ClientServices/Authorize/UserService.cs create mode 100644 src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs create mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs create mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs create mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs create mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs create mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs create mode 100644 src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor create mode 100644 src/OTManager.Web/Components/Account/Pages/Auth/Login.razor create mode 100644 src/OTManager.Web/Components/Account/Pages/Auth/Register.razor create mode 100644 src/OTManager.Web/Components/Account/Pages/User/Profile.razor create mode 100644 src/OTManager.Web/Components/Clients/Components/ContextMenuComponent.razor create mode 100644 src/OTManager.Web/Components/Clients/Components/OverseeMenuComponent.razor create mode 100644 src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs create mode 100644 src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs create mode 100644 src/OTManager.Web/wwwroot/Docs/LICENSE.txt create mode 100644 src/OTManager.Web/wwwroot/Docs/README.md diff --git a/OTManager.Data/Account/ApplicationRole.cs b/OTManager.Data/Account/ApplicationRole.cs new file mode 100644 index 0000000..10e8046 --- /dev/null +++ b/OTManager.Data/Account/ApplicationRole.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Microsoft.AspNetCore.Identity; + +namespace OTManager.Data.Account; + +public class ApplicationRole : IdentityRole +{ +} diff --git a/OTManager.Data/Account/ApplicationUser.cs b/OTManager.Data/Account/ApplicationUser.cs new file mode 100644 index 0000000..653cf08 --- /dev/null +++ b/OTManager.Data/Account/ApplicationUser.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Identity; + +namespace OTManager.Data.Account; + +public class ApplicationUser : IdentityUser +{ + +} \ No newline at end of file diff --git a/OTManager.Data/Context/ApplicationDbContext.cs b/OTManager.Data/Context/ApplicationDbContext.cs index 0f01fc5..fb78ae2 100644 --- a/OTManager.Data/Context/ApplicationDbContext.cs +++ b/OTManager.Data/Context/ApplicationDbContext.cs @@ -1,26 +1,35 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using OTManager.Core.Entities.OT; +using OTManager.Data.Account; using OTManager.Data.Audites; namespace OTManager.Data.Context; -public class ApplicationDbContext(DbContextOptions options) - : DbContext(options) +public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext, Guid>(options) { public DbSet Clients => Set(); public DbSet Features => Set(); public DbSet Materials => Set(); - public DbSet MaterialCosts => Set(); public DbSet Orders => Set(); public DbSet Services => Set(); - public DbSet ServiceCosts => Set(); public DbSet Workers => Set(); - public DbSet WorkerCosts => Set(); - protected override void OnModelCreating(ModelBuilder modelBuilder) + protected override void OnModelCreating(ModelBuilder builder) { - modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly); + builder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly); + + builder.Entity>(e => { + e.HasKey(l => new { l.LoginProvider, l.ProviderKey }); + }); + builder.Entity>(e => { + e.HasKey(r => new { r.UserId, r.RoleId }); + }); + builder.Entity>(e => { + e.HasKey(t => new { t.UserId, t.LoginProvider, t.Name }); + }); } public override int SaveChanges() diff --git a/OTManager.Data/Migrations/20251009232432_InitialCommit.Designer.cs b/OTManager.Data/Migrations/20251009232432_InitialCommit.Designer.cs deleted file mode 100644 index b6593d7..0000000 --- a/OTManager.Data/Migrations/20251009232432_InitialCommit.Designer.cs +++ /dev/null @@ -1,541 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using OTManager.Data.Context; - -#nullable disable - -namespace OTManager.Data.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20251009232432_InitialCommit")] - partial class InitialCommit - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.9") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(100) - .HasColumnType("nvarchar(100)"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("Code"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Name"); - - b.ToTable("Clients"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Facture", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ClientId") - .HasColumnType("uniqueidentifier"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("nvarchar(10)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("OrderId") - .HasColumnType("uniqueidentifier"); - - b.Property("TotalPrice") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("Code"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("OrderId"); - - b.ToTable("Features"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Material", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("nvarchar(32)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("MeasureUnit") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("UnitCost") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("Code"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Name"); - - b.ToTable("Materials"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.MaterialCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("Code") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("nvarchar(32)"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("MeasureUnit") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("OrderId") - .HasColumnType("uniqueidentifier"); - - b.Property("Quantity") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,6)") - .HasDefaultValue(0m); - - b.Property("TotalCost") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); - - b.Property("UnitCost") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("Code"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Name"); - - b.HasIndex("OrderId"); - - b.ToTable("MaterialCosts"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ClientId") - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("OrderNumber") - .IsRequired() - .HasMaxLength(10) - .HasColumnType("nvarchar(10)"); - - b.Property("OrderType") - .HasColumnType("int"); - - b.Property("Status") - .HasColumnType("bit"); - - b.Property("TotalCost") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("OrderNumber"); - - b.ToTable("Orders"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.OrderService", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Price") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Name"); - - b.ToTable("Services"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("OrderId") - .HasColumnType("uniqueidentifier"); - - b.Property("Price") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(18,2)") - .HasDefaultValue(0m); - - b.Property("Quantity") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(1); - - b.Property("TotalPrice") - .HasColumnType("decimal(18,2)"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Name"); - - b.HasIndex("OrderId"); - - b.ToTable("ServiceCosts"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Worker", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("HourlyRate") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") - .HasDefaultValue(0m); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Name"); - - b.ToTable("Workers"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.WorkerCost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetime2"); - - b.Property("CreatedBy") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("HourlyRate") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") - .HasDefaultValue(0m); - - b.Property("HoursWorked") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") - .HasDefaultValue(0m); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("OrderId") - .HasColumnType("uniqueidentifier"); - - b.Property("TotalCost") - .ValueGeneratedOnAdd() - .HasColumnType("decimal(2,4)") - .HasDefaultValue(0m); - - b.Property("UpdatedAt") - .HasColumnType("datetime2"); - - b.Property("UpdatedBy") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("Name"); - - b.HasIndex("OrderId"); - - b.ToTable("WorkerCosts"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Facture", b => - { - b.HasOne("OTManager.Core.Entities.OT.Client", "Client") - .WithMany("Factures") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("OTManager.Core.Entities.OT.Order", "Order") - .WithMany() - .HasForeignKey("OrderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Order"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.MaterialCost", b => - { - b.HasOne("OTManager.Core.Entities.OT.Order", "Order") - .WithMany("Materials") - .HasForeignKey("OrderId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Order"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Order", b => - { - b.HasOne("OTManager.Core.Entities.OT.Client", "Client") - .WithMany("Orders") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceCost", b => - { - b.HasOne("OTManager.Core.Entities.OT.Order", "Order") - .WithMany("Services") - .HasForeignKey("OrderId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Order"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.WorkerCost", b => - { - b.HasOne("OTManager.Core.Entities.OT.Order", "Order") - .WithMany("Workers") - .HasForeignKey("OrderId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Order"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Client", b => - { - b.Navigation("Factures"); - - b.Navigation("Orders"); - }); - - modelBuilder.Entity("OTManager.Core.Entities.OT.Order", b => - { - b.Navigation("Materials"); - - b.Navigation("Services"); - - b.Navigation("Workers"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs b/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs deleted file mode 100644 index 3176273..0000000 --- a/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.cs +++ /dev/null @@ -1,777 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace OTManager.Data.Migrations -{ - /// - public partial class ModelsConfigsUpdates : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Features_Clients_ClientId", - table: "Features"); - - migrationBuilder.DropForeignKey( - name: "FK_Features_Orders_OrderId", - table: "Features"); - - migrationBuilder.DropForeignKey( - name: "FK_MaterialCosts_Orders_OrderId", - table: "MaterialCosts"); - - migrationBuilder.DropForeignKey( - name: "FK_Orders_Clients_ClientId", - table: "Orders"); - - migrationBuilder.DropForeignKey( - name: "FK_ServiceCosts_Orders_OrderId", - table: "ServiceCosts"); - - migrationBuilder.DropForeignKey( - name: "FK_WorkerCosts_Orders_OrderId", - table: "WorkerCosts"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Orders", - table: "Orders"); - - migrationBuilder.DropIndex( - name: "IX_Orders_OrderNumber", - table: "Orders"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Materials", - table: "Materials"); - - migrationBuilder.DropIndex( - name: "IX_Materials_Code", - table: "Materials"); - - migrationBuilder.DropPrimaryKey( - name: "PK_MaterialCosts", - table: "MaterialCosts"); - - migrationBuilder.DropIndex( - name: "IX_MaterialCosts_Code", - table: "MaterialCosts"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Features", - table: "Features"); - - migrationBuilder.DropIndex( - name: "IX_Features_Code", - table: "Features"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Clients", - table: "Clients"); - - migrationBuilder.DropIndex( - name: "IX_Clients_Code", - table: "Clients"); - - migrationBuilder.RenameTable( - name: "Orders", - newName: "Order"); - - migrationBuilder.RenameTable( - name: "Materials", - newName: "Material"); - - migrationBuilder.RenameTable( - name: "MaterialCosts", - newName: "MaterialCost"); - - migrationBuilder.RenameTable( - name: "Features", - newName: "Facture"); - - migrationBuilder.RenameTable( - name: "Clients", - newName: "Client"); - - migrationBuilder.RenameIndex( - name: "IX_Orders_CreatedAt", - table: "Order", - newName: "IX_Order_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_Orders_ClientId", - table: "Order", - newName: "IX_Order_ClientId"); - - migrationBuilder.RenameIndex( - name: "IX_Materials_Name", - table: "Material", - newName: "IX_Material_Name"); - - migrationBuilder.RenameIndex( - name: "IX_Materials_CreatedAt", - table: "Material", - newName: "IX_Material_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_MaterialCosts_OrderId", - table: "MaterialCost", - newName: "IX_MaterialCost_OrderId"); - - migrationBuilder.RenameIndex( - name: "IX_MaterialCosts_Name", - table: "MaterialCost", - newName: "IX_MaterialCost_Name"); - - migrationBuilder.RenameIndex( - name: "IX_MaterialCosts_CreatedAt", - table: "MaterialCost", - newName: "IX_MaterialCost_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_Features_OrderId", - table: "Facture", - newName: "IX_Facture_OrderId"); - - migrationBuilder.RenameIndex( - name: "IX_Features_CreatedAt", - table: "Facture", - newName: "IX_Facture_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_Features_ClientId", - table: "Facture", - newName: "IX_Facture_ClientId"); - - migrationBuilder.RenameIndex( - name: "IX_Clients_Name", - table: "Client", - newName: "IX_Client_Name"); - - migrationBuilder.RenameIndex( - name: "IX_Clients_CreatedAt", - table: "Client", - newName: "IX_Client_CreatedAt"); - - migrationBuilder.AlterColumn( - name: "HourlyRate", - table: "Workers", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "TotalCost", - table: "WorkerCosts", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "HoursWorked", - table: "WorkerCosts", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "HourlyRate", - table: "WorkerCosts", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Order", - type: "nvarchar(100)", - maxLength: 100, - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Order", - type: "nvarchar(100)", - maxLength: 100, - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(max)"); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Material", - type: "nvarchar(100)", - maxLength: 100, - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Material", - type: "nvarchar(100)", - maxLength: 100, - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(max)"); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "MaterialCost", - type: "nvarchar(100)", - maxLength: 100, - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "MaterialCost", - type: "nvarchar(100)", - maxLength: 100, - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(max)"); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Facture", - type: "nvarchar(100)", - maxLength: 100, - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Facture", - type: "nvarchar(100)", - maxLength: 100, - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(max)"); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Client", - type: "nvarchar(100)", - maxLength: 100, - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Client", - type: "nvarchar(100)", - maxLength: 100, - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(max)"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Order", - table: "Order", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Material", - table: "Material", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_MaterialCost", - table: "MaterialCost", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Facture", - table: "Facture", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Client", - table: "Client", - column: "Id"); - - migrationBuilder.CreateIndex( - name: "IX_Order_OrderNumber", - table: "Order", - column: "OrderNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Material_Code", - table: "Material", - column: "Code", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_MaterialCost_Code", - table: "MaterialCost", - column: "Code", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Facture_Code", - table: "Facture", - column: "Code", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Client_Code", - table: "Client", - column: "Code", - unique: true); - - migrationBuilder.AddForeignKey( - name: "FK_Facture_Client_ClientId", - table: "Facture", - column: "ClientId", - principalTable: "Client", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_Facture_Order_OrderId", - table: "Facture", - column: "OrderId", - principalTable: "Order", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - - migrationBuilder.AddForeignKey( - name: "FK_MaterialCost_Order_OrderId", - table: "MaterialCost", - column: "OrderId", - principalTable: "Order", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_Order_Client_ClientId", - table: "Order", - column: "ClientId", - principalTable: "Client", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_ServiceCosts_Order_OrderId", - table: "ServiceCosts", - column: "OrderId", - principalTable: "Order", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_WorkerCosts_Order_OrderId", - table: "WorkerCosts", - column: "OrderId", - principalTable: "Order", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_Facture_Client_ClientId", - table: "Facture"); - - migrationBuilder.DropForeignKey( - name: "FK_Facture_Order_OrderId", - table: "Facture"); - - migrationBuilder.DropForeignKey( - name: "FK_MaterialCost_Order_OrderId", - table: "MaterialCost"); - - migrationBuilder.DropForeignKey( - name: "FK_Order_Client_ClientId", - table: "Order"); - - migrationBuilder.DropForeignKey( - name: "FK_ServiceCosts_Order_OrderId", - table: "ServiceCosts"); - - migrationBuilder.DropForeignKey( - name: "FK_WorkerCosts_Order_OrderId", - table: "WorkerCosts"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Order", - table: "Order"); - - migrationBuilder.DropIndex( - name: "IX_Order_OrderNumber", - table: "Order"); - - migrationBuilder.DropPrimaryKey( - name: "PK_MaterialCost", - table: "MaterialCost"); - - migrationBuilder.DropIndex( - name: "IX_MaterialCost_Code", - table: "MaterialCost"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Material", - table: "Material"); - - migrationBuilder.DropIndex( - name: "IX_Material_Code", - table: "Material"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Facture", - table: "Facture"); - - migrationBuilder.DropIndex( - name: "IX_Facture_Code", - table: "Facture"); - - migrationBuilder.DropPrimaryKey( - name: "PK_Client", - table: "Client"); - - migrationBuilder.DropIndex( - name: "IX_Client_Code", - table: "Client"); - - migrationBuilder.RenameTable( - name: "Order", - newName: "Orders"); - - migrationBuilder.RenameTable( - name: "MaterialCost", - newName: "MaterialCosts"); - - migrationBuilder.RenameTable( - name: "Material", - newName: "Materials"); - - migrationBuilder.RenameTable( - name: "Facture", - newName: "Features"); - - migrationBuilder.RenameTable( - name: "Client", - newName: "Clients"); - - migrationBuilder.RenameIndex( - name: "IX_Order_CreatedAt", - table: "Orders", - newName: "IX_Orders_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_Order_ClientId", - table: "Orders", - newName: "IX_Orders_ClientId"); - - migrationBuilder.RenameIndex( - name: "IX_MaterialCost_OrderId", - table: "MaterialCosts", - newName: "IX_MaterialCosts_OrderId"); - - migrationBuilder.RenameIndex( - name: "IX_MaterialCost_Name", - table: "MaterialCosts", - newName: "IX_MaterialCosts_Name"); - - migrationBuilder.RenameIndex( - name: "IX_MaterialCost_CreatedAt", - table: "MaterialCosts", - newName: "IX_MaterialCosts_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_Material_Name", - table: "Materials", - newName: "IX_Materials_Name"); - - migrationBuilder.RenameIndex( - name: "IX_Material_CreatedAt", - table: "Materials", - newName: "IX_Materials_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_Facture_OrderId", - table: "Features", - newName: "IX_Features_OrderId"); - - migrationBuilder.RenameIndex( - name: "IX_Facture_CreatedAt", - table: "Features", - newName: "IX_Features_CreatedAt"); - - migrationBuilder.RenameIndex( - name: "IX_Facture_ClientId", - table: "Features", - newName: "IX_Features_ClientId"); - - migrationBuilder.RenameIndex( - name: "IX_Client_Name", - table: "Clients", - newName: "IX_Clients_Name"); - - migrationBuilder.RenameIndex( - name: "IX_Client_CreatedAt", - table: "Clients", - newName: "IX_Clients_CreatedAt"); - - migrationBuilder.AlterColumn( - name: "HourlyRate", - table: "Workers", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "TotalCost", - table: "WorkerCosts", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "HoursWorked", - table: "WorkerCosts", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "HourlyRate", - table: "WorkerCosts", - type: "decimal(2,2)", - nullable: false, - defaultValue: 0m, - oldClrType: typeof(decimal), - oldType: "decimal(2,2)", - oldDefaultValue: 0m); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Orders", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100, - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Orders", - type: "nvarchar(max)", - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "MaterialCosts", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100, - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "MaterialCosts", - type: "nvarchar(max)", - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Materials", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100, - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Materials", - type: "nvarchar(max)", - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Features", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100, - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Features", - type: "nvarchar(max)", - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100); - - migrationBuilder.AlterColumn( - name: "UpdatedBy", - table: "Clients", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100, - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "CreatedBy", - table: "Clients", - type: "nvarchar(max)", - nullable: false, - oldClrType: typeof(string), - oldType: "nvarchar(100)", - oldMaxLength: 100); - - migrationBuilder.AddPrimaryKey( - name: "PK_Orders", - table: "Orders", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_MaterialCosts", - table: "MaterialCosts", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Materials", - table: "Materials", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Features", - table: "Features", - column: "Id"); - - migrationBuilder.AddPrimaryKey( - name: "PK_Clients", - table: "Clients", - column: "Id"); - - migrationBuilder.CreateIndex( - name: "IX_Orders_OrderNumber", - table: "Orders", - column: "OrderNumber"); - - migrationBuilder.CreateIndex( - name: "IX_MaterialCosts_Code", - table: "MaterialCosts", - column: "Code"); - - migrationBuilder.CreateIndex( - name: "IX_Materials_Code", - table: "Materials", - column: "Code"); - - migrationBuilder.CreateIndex( - name: "IX_Features_Code", - table: "Features", - column: "Code"); - - migrationBuilder.CreateIndex( - name: "IX_Clients_Code", - table: "Clients", - column: "Code"); - - migrationBuilder.AddForeignKey( - name: "FK_Features_Clients_ClientId", - table: "Features", - column: "ClientId", - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_Features_Orders_OrderId", - table: "Features", - column: "OrderId", - principalTable: "Orders", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - - migrationBuilder.AddForeignKey( - name: "FK_MaterialCosts_Orders_OrderId", - table: "MaterialCosts", - column: "OrderId", - principalTable: "Orders", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_Orders_Clients_ClientId", - table: "Orders", - column: "ClientId", - principalTable: "Clients", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_ServiceCosts_Orders_OrderId", - table: "ServiceCosts", - column: "OrderId", - principalTable: "Orders", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - - migrationBuilder.AddForeignKey( - name: "FK_WorkerCosts_Orders_OrderId", - table: "WorkerCosts", - column: "OrderId", - principalTable: "Orders", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - } - } -} diff --git a/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.Designer.cs b/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.Designer.cs similarity index 75% rename from OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.Designer.cs rename to OTManager.Data/Migrations/20251015220442_AuthenticationCommit.Designer.cs index 99709e9..e30b766 100644 --- a/OTManager.Data/Migrations/20251010212249_Models&ConfigsUpdates.Designer.cs +++ b/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.Designer.cs @@ -12,8 +12,8 @@ namespace OTManager.Data.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20251010212249_Models&ConfigsUpdates")] - partial class ModelsConfigsUpdates + [Migration("20251015220442_AuthenticationCommit")] + partial class AuthenticationCommit { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -25,6 +25,101 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("RoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("UserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.ToTable("UserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.ToTable("UserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserTokens"); + }); + modelBuilder.Entity("OTManager.Core.Entities.OT.Client", b => { b.Property("Id") @@ -339,7 +434,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("OrderId"); - b.ToTable("ServiceCosts"); + b.ToTable("ServiceCost"); }); modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceOrder", b => @@ -469,7 +564,80 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("OrderId"); - b.ToTable("WorkerCosts"); + b.ToTable("WorkerCost"); + }); + + modelBuilder.Entity("OTManager.Data.Context.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("NormalizedName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("OTManager.Data.Context.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("NormalizedUserName") + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Users"); }); modelBuilder.Entity("OTManager.Core.Entities.OT.Facture", b => diff --git a/OTManager.Data/Migrations/20251009232432_InitialCommit.cs b/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.cs similarity index 55% rename from OTManager.Data/Migrations/20251009232432_InitialCommit.cs rename to OTManager.Data/Migrations/20251015220442_AuthenticationCommit.cs index 5eb8e6a..0c3a0f6 100644 --- a/OTManager.Data/Migrations/20251009232432_InitialCommit.cs +++ b/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.cs @@ -1,49 +1,79 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using System; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace OTManager.Data.Migrations { /// - public partial class InitialCommit : Migration + public partial class AuthenticationCommit : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( - name: "Clients", + name: "Client", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), Code = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), CreatedAt = table.Column(type: "datetime2", nullable: true), - CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), UpdatedAt = table.Column(type: "datetime2", nullable: true), - UpdatedBy = table.Column(type: "nvarchar(max)", nullable: true) + UpdatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true) }, constraints: table => { - table.PrimaryKey("PK_Clients", x => x.Id); + table.PrimaryKey("PK_Client", x => x.Id); }); migrationBuilder.CreateTable( - name: "Materials", + name: "Material", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), Code = table.Column(type: "nvarchar(32)", maxLength: 32, nullable: false), Name = table.Column(type: "nvarchar(450)", nullable: false), MeasureUnit = table.Column(type: "nvarchar(max)", nullable: false), - UnitCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0m), + UnitCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0.00m), CreatedAt = table.Column(type: "datetime2", nullable: true), - CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), UpdatedAt = table.Column(type: "datetime2", nullable: true), - UpdatedBy = table.Column(type: "nvarchar(max)", nullable: true) + UpdatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Material", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "RoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) }, constraints: table => { - table.PrimaryKey("PK_Materials", x => x.Id); + table.PrimaryKey("PK_RoleClaims", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Roles", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: true), + NormalizedName = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Roles", x => x.Id); }); migrationBuilder.CreateTable( @@ -64,6 +94,86 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_Services", x => x.Id); }); + migrationBuilder.CreateTable( + name: "UserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UserClaims", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "UserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserLogins", x => new { x.LoginProvider, x.ProviderKey }); + }); + + migrationBuilder.CreateTable( + name: "UserRoles", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserRoles", x => new { x.UserId, x.RoleId }); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + UserName = table.Column(type: "nvarchar(max)", nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(max)", nullable: true), + Email = table.Column(type: "nvarchar(max)", nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(max)", nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "UserTokens", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + }); + migrationBuilder.CreateTable( name: "Workers", columns: table => new @@ -82,7 +192,7 @@ protected override void Up(MigrationBuilder migrationBuilder) }); migrationBuilder.CreateTable( - name: "Orders", + name: "Order", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), @@ -90,84 +200,84 @@ protected override void Up(MigrationBuilder migrationBuilder) OrderNumber = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: false), ClientId = table.Column(type: "uniqueidentifier", nullable: false), Status = table.Column(type: "bit", nullable: false), - TotalCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0m), + TotalCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0.00m), CreatedAt = table.Column(type: "datetime2", nullable: true), - CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), UpdatedAt = table.Column(type: "datetime2", nullable: true), - UpdatedBy = table.Column(type: "nvarchar(max)", nullable: true) + UpdatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true) }, constraints: table => { - table.PrimaryKey("PK_Orders", x => x.Id); + table.PrimaryKey("PK_Order", x => x.Id); table.ForeignKey( - name: "FK_Orders_Clients_ClientId", + name: "FK_Order_Client_ClientId", column: x => x.ClientId, - principalTable: "Clients", + principalTable: "Client", principalColumn: "Id", onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateTable( - name: "Features", + name: "Facture", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), Code = table.Column(type: "nvarchar(10)", maxLength: 10, nullable: false), ClientId = table.Column(type: "uniqueidentifier", nullable: false), OrderId = table.Column(type: "uniqueidentifier", nullable: false), - TotalPrice = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0m), + TotalPrice = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0.00m), CreatedAt = table.Column(type: "datetime2", nullable: true), - CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), UpdatedAt = table.Column(type: "datetime2", nullable: true), - UpdatedBy = table.Column(type: "nvarchar(max)", nullable: true) + UpdatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true) }, constraints: table => { - table.PrimaryKey("PK_Features", x => x.Id); + table.PrimaryKey("PK_Facture", x => x.Id); table.ForeignKey( - name: "FK_Features_Clients_ClientId", + name: "FK_Facture_Client_ClientId", column: x => x.ClientId, - principalTable: "Clients", + principalTable: "Client", principalColumn: "Id", onDelete: ReferentialAction.Restrict); table.ForeignKey( - name: "FK_Features_Orders_OrderId", + name: "FK_Facture_Order_OrderId", column: x => x.OrderId, - principalTable: "Orders", + principalTable: "Order", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( - name: "MaterialCosts", + name: "MaterialCost", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), Code = table.Column(type: "nvarchar(32)", maxLength: 32, nullable: false), Name = table.Column(type: "nvarchar(450)", nullable: false), MeasureUnit = table.Column(type: "nvarchar(max)", nullable: false), - UnitCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0m), + UnitCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0.00m), OrderId = table.Column(type: "uniqueidentifier", nullable: false), - Quantity = table.Column(type: "decimal(18,6)", nullable: false, defaultValue: 0m), - TotalCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0m), + Quantity = table.Column(type: "decimal(18,6)", nullable: false, defaultValue: 0.00m), + TotalCost = table.Column(type: "decimal(18,2)", nullable: false, defaultValue: 0.00m), CreatedAt = table.Column(type: "datetime2", nullable: true), - CreatedBy = table.Column(type: "nvarchar(max)", nullable: false), + CreatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), UpdatedAt = table.Column(type: "datetime2", nullable: true), - UpdatedBy = table.Column(type: "nvarchar(max)", nullable: true) + UpdatedBy = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: true) }, constraints: table => { - table.PrimaryKey("PK_MaterialCosts", x => x.Id); + table.PrimaryKey("PK_MaterialCost", x => x.Id); table.ForeignKey( - name: "FK_MaterialCosts_Orders_OrderId", + name: "FK_MaterialCost_Order_OrderId", column: x => x.OrderId, - principalTable: "Orders", + principalTable: "Order", principalColumn: "Id", onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateTable( - name: "ServiceCosts", + name: "ServiceCost", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), @@ -184,17 +294,17 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_ServiceCosts", x => x.Id); + table.PrimaryKey("PK_ServiceCost", x => x.Id); table.ForeignKey( - name: "FK_ServiceCosts_Orders_OrderId", + name: "FK_ServiceCost_Order_OrderId", column: x => x.OrderId, - principalTable: "Orders", + principalTable: "Order", principalColumn: "Id", onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateTable( - name: "WorkerCosts", + name: "WorkerCost", columns: table => new { Id = table.Column(type: "uniqueidentifier", nullable: false), @@ -210,113 +320,118 @@ protected override void Up(MigrationBuilder migrationBuilder) }, constraints: table => { - table.PrimaryKey("PK_WorkerCosts", x => x.Id); + table.PrimaryKey("PK_WorkerCost", x => x.Id); table.ForeignKey( - name: "FK_WorkerCosts_Orders_OrderId", + name: "FK_WorkerCost_Order_OrderId", column: x => x.OrderId, - principalTable: "Orders", + principalTable: "Order", principalColumn: "Id", onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateIndex( - name: "IX_Clients_Code", - table: "Clients", - column: "Code"); + name: "IX_Client_Code", + table: "Client", + column: "Code", + unique: true); migrationBuilder.CreateIndex( - name: "IX_Clients_CreatedAt", - table: "Clients", + name: "IX_Client_CreatedAt", + table: "Client", column: "CreatedAt"); migrationBuilder.CreateIndex( - name: "IX_Clients_Name", - table: "Clients", + name: "IX_Client_Name", + table: "Client", column: "Name"); migrationBuilder.CreateIndex( - name: "IX_Features_ClientId", - table: "Features", + name: "IX_Facture_ClientId", + table: "Facture", column: "ClientId"); migrationBuilder.CreateIndex( - name: "IX_Features_Code", - table: "Features", - column: "Code"); + name: "IX_Facture_Code", + table: "Facture", + column: "Code", + unique: true); migrationBuilder.CreateIndex( - name: "IX_Features_CreatedAt", - table: "Features", + name: "IX_Facture_CreatedAt", + table: "Facture", column: "CreatedAt"); migrationBuilder.CreateIndex( - name: "IX_Features_OrderId", - table: "Features", + name: "IX_Facture_OrderId", + table: "Facture", column: "OrderId"); migrationBuilder.CreateIndex( - name: "IX_MaterialCosts_Code", - table: "MaterialCosts", - column: "Code"); + name: "IX_Material_Code", + table: "Material", + column: "Code", + unique: true); migrationBuilder.CreateIndex( - name: "IX_MaterialCosts_CreatedAt", - table: "MaterialCosts", + name: "IX_Material_CreatedAt", + table: "Material", column: "CreatedAt"); migrationBuilder.CreateIndex( - name: "IX_MaterialCosts_Name", - table: "MaterialCosts", + name: "IX_Material_Name", + table: "Material", column: "Name"); migrationBuilder.CreateIndex( - name: "IX_MaterialCosts_OrderId", - table: "MaterialCosts", - column: "OrderId"); - - migrationBuilder.CreateIndex( - name: "IX_Materials_Code", - table: "Materials", - column: "Code"); + name: "IX_MaterialCost_Code", + table: "MaterialCost", + column: "Code", + unique: true); migrationBuilder.CreateIndex( - name: "IX_Materials_CreatedAt", - table: "Materials", + name: "IX_MaterialCost_CreatedAt", + table: "MaterialCost", column: "CreatedAt"); migrationBuilder.CreateIndex( - name: "IX_Materials_Name", - table: "Materials", + name: "IX_MaterialCost_Name", + table: "MaterialCost", column: "Name"); migrationBuilder.CreateIndex( - name: "IX_Orders_ClientId", - table: "Orders", + name: "IX_MaterialCost_OrderId", + table: "MaterialCost", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_ClientId", + table: "Order", column: "ClientId"); migrationBuilder.CreateIndex( - name: "IX_Orders_CreatedAt", - table: "Orders", + name: "IX_Order_CreatedAt", + table: "Order", column: "CreatedAt"); migrationBuilder.CreateIndex( - name: "IX_Orders_OrderNumber", - table: "Orders", - column: "OrderNumber"); + name: "IX_Order_OrderNumber", + table: "Order", + column: "OrderNumber", + unique: true); migrationBuilder.CreateIndex( - name: "IX_ServiceCosts_CreatedAt", - table: "ServiceCosts", + name: "IX_ServiceCost_CreatedAt", + table: "ServiceCost", column: "CreatedAt"); migrationBuilder.CreateIndex( - name: "IX_ServiceCosts_Name", - table: "ServiceCosts", + name: "IX_ServiceCost_Name", + table: "ServiceCost", column: "Name"); migrationBuilder.CreateIndex( - name: "IX_ServiceCosts_OrderId", - table: "ServiceCosts", + name: "IX_ServiceCost_OrderId", + table: "ServiceCost", column: "OrderId"); migrationBuilder.CreateIndex( @@ -330,18 +445,18 @@ protected override void Up(MigrationBuilder migrationBuilder) column: "Name"); migrationBuilder.CreateIndex( - name: "IX_WorkerCosts_CreatedAt", - table: "WorkerCosts", + name: "IX_WorkerCost_CreatedAt", + table: "WorkerCost", column: "CreatedAt"); migrationBuilder.CreateIndex( - name: "IX_WorkerCosts_Name", - table: "WorkerCosts", + name: "IX_WorkerCost_Name", + table: "WorkerCost", column: "Name"); migrationBuilder.CreateIndex( - name: "IX_WorkerCosts_OrderId", - table: "WorkerCosts", + name: "IX_WorkerCost_OrderId", + table: "WorkerCost", column: "OrderId"); migrationBuilder.CreateIndex( @@ -359,31 +474,52 @@ protected override void Up(MigrationBuilder migrationBuilder) protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "Features"); + name: "Facture"); + + migrationBuilder.DropTable( + name: "Material"); + + migrationBuilder.DropTable( + name: "MaterialCost"); migrationBuilder.DropTable( - name: "MaterialCosts"); + name: "RoleClaims"); migrationBuilder.DropTable( - name: "Materials"); + name: "Roles"); migrationBuilder.DropTable( - name: "ServiceCosts"); + name: "ServiceCost"); migrationBuilder.DropTable( name: "Services"); migrationBuilder.DropTable( - name: "WorkerCosts"); + name: "UserClaims"); + + migrationBuilder.DropTable( + name: "UserLogins"); + + migrationBuilder.DropTable( + name: "UserRoles"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "UserTokens"); + + migrationBuilder.DropTable( + name: "WorkerCost"); migrationBuilder.DropTable( name: "Workers"); migrationBuilder.DropTable( - name: "Orders"); + name: "Order"); migrationBuilder.DropTable( - name: "Clients"); + name: "Client"); } } } diff --git a/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs b/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs index ed707d4..5fc0719 100644 --- a/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/OTManager.Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -22,6 +22,101 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("RoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("UserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.ToTable("UserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.ToTable("UserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserTokens"); + }); + modelBuilder.Entity("OTManager.Core.Entities.OT.Client", b => { b.Property("Id") @@ -336,7 +431,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("OrderId"); - b.ToTable("ServiceCosts"); + b.ToTable("ServiceCost"); }); modelBuilder.Entity("OTManager.Core.Entities.OT.ServiceOrder", b => @@ -466,7 +561,80 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("OrderId"); - b.ToTable("WorkerCosts"); + b.ToTable("WorkerCost"); + }); + + modelBuilder.Entity("OTManager.Data.Context.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("NormalizedName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("OTManager.Data.Context.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("NormalizedUserName") + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Users"); }); modelBuilder.Entity("OTManager.Core.Entities.OT.Facture", b => diff --git a/OTManager.Data/OTManager.Data.csproj b/OTManager.Data/OTManager.Data.csproj index 2dbbf68..8ef5a3a 100644 --- a/OTManager.Data/OTManager.Data.csproj +++ b/OTManager.Data/OTManager.Data.csproj @@ -7,10 +7,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -20,4 +20,8 @@ + + + + diff --git a/OTManager.Data/Repositories/Implements/MaterialCostRepository.cs b/OTManager.Data/Repositories/Implements/MaterialCostRepository.cs deleted file mode 100644 index ac8c779..0000000 --- a/OTManager.Data/Repositories/Implements/MaterialCostRepository.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -using OTManager.Core.Entities.OT; -using OTManager.Core.QueryParams; -using OTManager.Data.Context; -using OTManager.Data.Repositories.Interface; - -namespace OTManager.Data.Repositories.Implements; - -public class MaterialCostRepository(ApplicationDbContext context) : IMaterialCostRepository -{ - private readonly ApplicationDbContext _context = context; - - public async Task AddAsync(MaterialCost entity) - { - await _context.MaterialCosts.AddAsync(entity); - await _context.SaveChangesAsync(); - } - - public async Task DeleteAsync(Guid id) - { - var entity = await GetByIdAsync(id); - if (entity is null) return; - _context.MaterialCosts.Remove(entity); - await _context.SaveChangesAsync(); - } - - public async Task> GetAllAsync() - => await _context.MaterialCosts.ToListAsync(); - - public async Task GetByIdAsync(Guid id) - => await _context.MaterialCosts.FirstOrDefaultAsync(f => f.Id == id); - - public async Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(MaterialCostQueryParams query) - { - var q = _context.MaterialCosts.AsQueryable(); - if (!string.IsNullOrWhiteSpace(query.Search)) - q = q.Where(m => m.Name.Contains(query.Search) || m.Code.Contains(query.Search)); - if (!string.IsNullOrWhiteSpace(query.Code)) - q = q.Where(m => m.Code == query.Code); - if (!string.IsNullOrWhiteSpace(query.Name)) - q = q.Where(m => m.Name == query.Name); - if (query.CreatedAt.HasValue) - q = q.Where(m => m.CreatedAt == query.CreatedAt); - var total = await q.CountAsync(); - if (query.Page > 0 && query.PageSize > 0) - q = q.Skip((query.Page - 1) * query.PageSize).Take(query.PageSize); - var items = await q.ToListAsync(); - return (items, total); - } - - public async Task UpdateAsync(MaterialCost entity) - { - _context.MaterialCosts.Update(entity); - await _context.SaveChangesAsync(); - } -} diff --git a/OTManager.Data/Repositories/Implements/ServiceCostRepository.cs b/OTManager.Data/Repositories/Implements/ServiceCostRepository.cs deleted file mode 100644 index 3029c87..0000000 --- a/OTManager.Data/Repositories/Implements/ServiceCostRepository.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -using OTManager.Core.Entities.OT; -using OTManager.Core.QueryParams; -using OTManager.Data.Context; -using OTManager.Data.Repositories.Interface; - -namespace OTManager.Data.Repositories.Implements; - -public class ServiceCostRepository(ApplicationDbContext context) : IServiceCostRepository -{ - private readonly ApplicationDbContext _context = context; - - public async Task AddAsync(ServiceCost entity) - { - await _context.ServiceCosts.AddAsync(entity); - await _context.SaveChangesAsync(); - } - - public async Task DeleteAsync(Guid id) - { - var entity = await GetByIdAsync(id); - if (entity is null) return; - _context.ServiceCosts.Remove(entity); - await _context.SaveChangesAsync(); - } - - public async Task> GetAllAsync() - => await _context.ServiceCosts.ToListAsync(); - - public async Task GetByIdAsync(Guid id) - => await _context.ServiceCosts.FirstOrDefaultAsync(f => f.Id == id); - - public async Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(ServiceCostQueryParams query) - { - var q = _context.ServiceCosts.AsQueryable(); - if (!string.IsNullOrWhiteSpace(query.Search)) - q = q.Where(s => s.Name.Contains(query.Search)); - if (!string.IsNullOrWhiteSpace(query.Name)) - q = q.Where(s => s.Name == query.Name); - if (query.CreatedAt.HasValue) - q = q.Where(s => s.CreatedAt == query.CreatedAt); - var total = await q.CountAsync(); - if (query.Page > 0 && query.PageSize > 0) - q = q.Skip((query.Page - 1) * query.PageSize).Take(query.PageSize); - var items = await q.ToListAsync(); - return (items, total); - } - - public async Task UpdateAsync(ServiceCost entity) - { - _context.ServiceCosts.Update(entity); - await _context.SaveChangesAsync(); - } -} diff --git a/OTManager.Data/Repositories/Implements/WorkerCostRepository.cs b/OTManager.Data/Repositories/Implements/WorkerCostRepository.cs deleted file mode 100644 index a7e2f42..0000000 --- a/OTManager.Data/Repositories/Implements/WorkerCostRepository.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Microsoft.EntityFrameworkCore; - -using OTManager.Core.Entities.OT; -using OTManager.Core.QueryParams; -using OTManager.Data.Context; -using OTManager.Data.Repositories.Interface; - -namespace OTManager.Data.Repositories.Implements; - -public class WorkerCostRepository(ApplicationDbContext context) : IWorkerCostRepository -{ - private readonly ApplicationDbContext _context = context; - - public async Task AddAsync(WorkerCost entity) - { - await _context.WorkerCosts.AddAsync(entity); - await _context.SaveChangesAsync(); - } - - public async Task DeleteAsync(Guid id) - { - var entity = await GetByIdAsync(id); - if (entity is null) return; - _context.WorkerCosts.Remove(entity); - await _context.SaveChangesAsync(); - } - - public async Task> GetAllAsync() - => await _context.WorkerCosts.ToListAsync(); - - public async Task GetByIdAsync(Guid id) - => await _context.WorkerCosts.FirstOrDefaultAsync(f => f.Id == id); - - public async Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(WorkerCostQueryParams query) - { - var q = _context.WorkerCosts.AsQueryable(); - if (!string.IsNullOrWhiteSpace(query.Search)) - q = q.Where(w => w.Name.Contains(query.Search)); - if (!string.IsNullOrWhiteSpace(query.Name)) - q = q.Where(w => w.Name == query.Name); - if (query.CreatedAt.HasValue) - q = q.Where(w => w.CreatedAt == query.CreatedAt); - var total = await q.CountAsync(); - if (query.Page > 0 && query.PageSize > 0) - q = q.Skip((query.Page - 1) * query.PageSize).Take(query.PageSize); - var items = await q.ToListAsync(); - return (items, total); - } - - public async Task UpdateAsync(WorkerCost entity) - { - _context.WorkerCosts.Update(entity); - await _context.SaveChangesAsync(); - } -} diff --git a/OTManager.Data/Repositories/Interface/IMaterialCostRepository.cs b/OTManager.Data/Repositories/Interface/IMaterialCostRepository.cs deleted file mode 100644 index 18ab5a2..0000000 --- a/OTManager.Data/Repositories/Interface/IMaterialCostRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using OTManager.Core.Entities.OT; -using OTManager.Core.QueryParams; -using OTManager.Data.Repositories.Abstracts; - -namespace OTManager.Data.Repositories.Interface; - -public interface IMaterialCostRepository : IRepository -{ - Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(MaterialCostQueryParams query); -} diff --git a/OTManager.Data/Repositories/Interface/IServiceCostRepository.cs b/OTManager.Data/Repositories/Interface/IServiceCostRepository.cs deleted file mode 100644 index 39e1acc..0000000 --- a/OTManager.Data/Repositories/Interface/IServiceCostRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using OTManager.Core.Entities.OT; -using OTManager.Core.QueryParams; -using OTManager.Data.Repositories.Abstracts; - -namespace OTManager.Data.Repositories.Interface; - -public interface IServiceCostRepository : IRepository -{ - Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(ServiceCostQueryParams query); -} diff --git a/OTManager.Data/Repositories/Interface/IWorkerCostRepository.cs b/OTManager.Data/Repositories/Interface/IWorkerCostRepository.cs deleted file mode 100644 index bb930b2..0000000 --- a/OTManager.Data/Repositories/Interface/IWorkerCostRepository.cs +++ /dev/null @@ -1,10 +0,0 @@ -using OTManager.Core.Entities.OT; -using OTManager.Core.QueryParams; -using OTManager.Data.Repositories.Abstracts; - -namespace OTManager.Data.Repositories.Interface; - -public interface IWorkerCostRepository : IRepository -{ - Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(WorkerCostQueryParams query); -} diff --git a/OTManager.Data/Repositories/RepositoryExtension.cs b/OTManager.Data/Repositories/RepositoryExtension.cs index 5560866..3063f46 100644 --- a/OTManager.Data/Repositories/RepositoryExtension.cs +++ b/OTManager.Data/Repositories/RepositoryExtension.cs @@ -14,10 +14,7 @@ public static IServiceCollection AddRepositoryExtension(this IServiceCollection services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); return services; } } diff --git a/OTManager.Data/UoW/IUnitOfWork.cs b/OTManager.Data/UoW/IUnitOfWork.cs index 493f825..c070160 100644 --- a/OTManager.Data/UoW/IUnitOfWork.cs +++ b/OTManager.Data/UoW/IUnitOfWork.cs @@ -13,9 +13,6 @@ public interface IUnitOfWork : IDisposable, IAsyncDisposable IClientRepository Clients { get; } IOrderRepository Orders { get; } IFactureRepository Factures { get; } - IServiceCostRepository ServiceCosts { get; } - IMaterialCostRepository MaterialCosts { get; } - IWorkerCostRepository WorkerCosts { get; } IMaterialRepository Materials { get; } IWorkerRepository Workers { get; } IOrderServiceRepository OrderServices { get; } diff --git a/OTManager.Data/UoW/UnitOfWork.cs b/OTManager.Data/UoW/UnitOfWork.cs index 3c67465..8ba30b1 100644 --- a/OTManager.Data/UoW/UnitOfWork.cs +++ b/OTManager.Data/UoW/UnitOfWork.cs @@ -32,12 +32,6 @@ public UnitOfWork(ApplicationDbContext context) /// public IFactureRepository Factures => new FactureRepository(_context); /// - public IServiceCostRepository ServiceCosts => new ServiceCostRepository(_context); - /// - public IMaterialCostRepository MaterialCosts => new MaterialCostRepository(_context); - /// - public IWorkerCostRepository WorkerCosts => new WorkerCostRepository(_context); - /// public IMaterialRepository Materials => new MaterialRepository(_context); /// public IWorkerRepository Workers => new WorkerRepository(_context); diff --git a/src/OTManager.Api/Account/Controllers/SessionController.cs b/src/OTManager.Api/Account/Controllers/SessionController.cs new file mode 100644 index 0000000..2faa7ea --- /dev/null +++ b/src/OTManager.Api/Account/Controllers/SessionController.cs @@ -0,0 +1,93 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; + +using OTManager.Api.Account.Dto; +using OTManager.Api.Account.Records; +using OTManager.Api.Account.Services.Interface; +using OTManager.Data.Account; + +namespace OTManager.Api.Account.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class SessionController(UserManager userManager, IConfiguration config, SignInManager signInManager, ISessionContext session, IJwtTokenService jwt) : ControllerBase +{ + private readonly UserManager _userManager = userManager; + private readonly SignInManager _signInManager = signInManager; + private readonly ISessionContext _session = session; + private readonly IConfiguration _config = config; + private readonly IJwtTokenService _jwt = jwt; + + public record RegisterDto(string Email, string Password); + public record LoginDto(string Email, string Password); + + [AllowAnonymous] + [HttpPost("register")] + public async Task Register([FromBody] RegisterDto model) + { + if (await _userManager.FindByEmailAsync(model.Email) != null) + { + return BadRequest("El correo ya está registrado."); + } + + var user = new ApplicationUser + { + UserName = model.Email, + Email = model.Email + }; + + var result = await _userManager.CreateAsync(user, model.Password); + + if (!result.Succeeded) + { + return BadRequest(result.Errors); + } + + // Asignar rol por defecto + await _userManager.AddToRoleAsync(user, "User"); + + return Ok("Usuario registrado correctamente"); + } + + [AllowAnonymous] + [HttpPost("login")] + public async Task>> Login([FromBody] LoginRequestDto req) + { + var correlationId = _session.GetCorrelationId(); + + var user = await _userManager.FindByNameAsync(req.Username); + if (user is null || !user.EmailConfirmed) + { + return Unauthorized(SessionResponse.Failure("Credenciales inválidas o usuario inactivo", null, correlationId)); + } + + var ok = await _userManager.CheckPasswordAsync(user, req.Password); + if (!ok) + { + // Bloqueo temporal después de varios intentos fallidos + await _userManager.AccessFailedAsync(user); + if (await _userManager.IsLockedOutAsync(user)) + { + return Unauthorized(SessionResponse.Failure("Cuenta bloqueada temporalmente por múltiples intentos fallidos", null, correlationId)); + } + + return Unauthorized(SessionResponse.Failure("Credenciales inválidas", null, correlationId)); + } + + // Reiniciar contador de accesos fallidos + await _userManager.ResetAccessFailedCountAsync(user); + + var (token, sessionInfo) = await _jwt.CreateTokenAsync(user); + return Ok(SessionResponse.Success(token, sessionInfo, correlationId)); + } + + [Authorize] + [HttpPost("logout")] + public async Task Logout() + { + // Invalidar el token si se utiliza almacenamiento de tokens + await _signInManager.SignOutAsync(); + return Ok(new { Message = "Sesión cerrada correctamente" }); + } +} diff --git a/src/OTManager.Api/Account/Controllers/UserController.cs b/src/OTManager.Api/Account/Controllers/UserController.cs new file mode 100644 index 0000000..25e7e56 --- /dev/null +++ b/src/OTManager.Api/Account/Controllers/UserController.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; + +using OTManager.Data.Account; + +namespace OTManager.Api.Account.Controllers; + +[ApiController] +[Route("api/[controller]")] +public class UserController(UserManager userManager, IConfiguration config, SignInManager signInManager) : ControllerBase +{ + private readonly UserManager _userManager = userManager; + private readonly SignInManager _signInManager = signInManager; + private readonly IConfiguration _config = config; +} diff --git a/src/OTManager.Api/Account/Dto/LoginRequestDto.cs b/src/OTManager.Api/Account/Dto/LoginRequestDto.cs new file mode 100644 index 0000000..70ac4bc --- /dev/null +++ b/src/OTManager.Api/Account/Dto/LoginRequestDto.cs @@ -0,0 +1,3 @@ +namespace OTManager.Api.Account.Dto; + +public record LoginRequestDto(string Username, string Password); diff --git a/src/OTManager.Api/Account/Extensions/IdentityExtension.cs b/src/OTManager.Api/Account/Extensions/IdentityExtension.cs new file mode 100644 index 0000000..10571fe --- /dev/null +++ b/src/OTManager.Api/Account/Extensions/IdentityExtension.cs @@ -0,0 +1,73 @@ +using System.Security.Claims; +using System.Text; + +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.IdentityModel.Tokens; + +using OTManager.Api.Account.Services.Implement; +using OTManager.Api.Account.Services.Interface; +using OTManager.Data.Account; +using OTManager.Data.Context; + +namespace OTManager.Api.Extensions; + +public static class IdentityExtension +{ + public static IServiceCollection AddAppIdentityAuthentication(this IServiceCollection services, IConfiguration configuration) + { + // Info: Add Identity + services.AddIdentityCore(opts => + { + // ToDo: Cambiar para ambientes de producción + opts.User.RequireUniqueEmail = true; + opts.Password.RequiredLength = 8; + opts.Password.RequireNonAlphanumeric = false; + opts.Password.RequireUppercase = false; + }) + .AddRoles>() + .AddEntityFrameworkStores() + .AddSignInManager>() + .AddDefaultTokenProviders(); + + // Info: Configurar Jwt + var jwtSettings = configuration.GetSection("Jwt"); + var key = Encoding.UTF8.GetBytes(jwtSettings["Key"]!); + + services.AddAuthentication(opt => + { + opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(opts => + { + opts.RequireHttpsMetadata = true; + opts.SaveToken = true; + opts.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidIssuer = jwtSettings["Issuer"], + ValidateAudience = true, + ValidAudience = jwtSettings["Audience"], + ValidateLifetime = true, + IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateIssuerSigningKey = true, + ClockSkew = TimeSpan.FromMinutes(1), + NameClaimType = ClaimTypes.Name, + RoleClaimType = ClaimTypes.Role + }; + }); + + // Info: Permisos dinámicos + // ! + //services.AddSingleton(); + //services.AddScoped(); + + services.AddScoped(); + services.AddScoped(); + services.AddHttpContextAccessor(); + + return services; + } +} diff --git a/src/OTManager.Api/Account/Records/SessionInfo.cs b/src/OTManager.Api/Account/Records/SessionInfo.cs new file mode 100644 index 0000000..737ac1e --- /dev/null +++ b/src/OTManager.Api/Account/Records/SessionInfo.cs @@ -0,0 +1,5 @@ +namespace OTManager.Api.Account.Records; + +public record SessionInfo +(Guid UserId, string Username, IReadOnlyList Roles, IReadOnlyList Permissions); + diff --git a/src/OTManager.Api/Account/Records/SessionResponse.cs b/src/OTManager.Api/Account/Records/SessionResponse.cs new file mode 100644 index 0000000..0ecfd90 --- /dev/null +++ b/src/OTManager.Api/Account/Records/SessionResponse.cs @@ -0,0 +1,17 @@ +namespace OTManager.Api.Account.Records +{ + public class SessionResponse + { + public bool IsSuccess { get; init; } + public string? Message { get; init; } + public T? Data { get; private init; } + public SessionInfo? Session { get; init; } + public string? CorrelationId { get; init; } + + public static SessionResponse Success(T data, SessionInfo session, string? cid = null) + => new() { IsSuccess = true, Data = data, Session = session, CorrelationId = cid }; + + public static SessionResponse Failure(string message, SessionInfo? session, string? cid = null) + => new() { IsSuccess = false, Message = message, Session = session, CorrelationId = cid }; + } +} diff --git a/src/OTManager.Api/Account/Records/TokenResponse.cs b/src/OTManager.Api/Account/Records/TokenResponse.cs new file mode 100644 index 0000000..1279b22 --- /dev/null +++ b/src/OTManager.Api/Account/Records/TokenResponse.cs @@ -0,0 +1,4 @@ +namespace OTManager.Api.Account.Records; + +public record TokenResponse +(string AccessToken, DateTime ExpireAtUtc); diff --git a/src/OTManager.Api/Account/Services/Implement/JwtTokenService.cs b/src/OTManager.Api/Account/Services/Implement/JwtTokenService.cs new file mode 100644 index 0000000..f9c9b40 --- /dev/null +++ b/src/OTManager.Api/Account/Services/Implement/JwtTokenService.cs @@ -0,0 +1,76 @@ + +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text; + +using Microsoft.AspNetCore.Identity; +using Microsoft.IdentityModel.Tokens; + +using OTManager.Api.Account.Records; +using OTManager.Api.Account.Services.Interface; +using OTManager.Data.Account; + +namespace OTManager.Api.Account.Services.Implement; + +public class JwtTokenService( + IConfiguration configuration, + UserManager users, + RoleManager> roles) : IJwtTokenService +{ + public async Task<(TokenResponse token, SessionInfo session)> CreateTokenAsync(ApplicationUser user) + { + var jwtSettings = configuration.GetSection("Jwt"); + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Key"]!)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + var now = DateTime.UtcNow; + + var userClaims = await users.GetClaimsAsync(user); + var roleNames = await users.GetRolesAsync(user); + var roleClaims = new List(); + + foreach (var roleName in roleNames) + { + var role = await roles.FindByNameAsync(roleName); + if (role is not null) + roleClaims.AddRange(await roles.GetClaimsAsync(role)); + } + + // Normaliza permisos: tipo "perm" + var perms = userClaims.Where(c => c.Type == "perm").Select(c => c.Value) + .Concat(roleClaims.Where(c => c.Type == "perm").Select(c => c.Value)) + .Distinct() + .ToList(); + + var claims = new List + { + new("sub", user.Id.ToString()), + new("name", user.UserName ?? ""), + new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), + new(JwtRegisteredClaimNames.Iat, EpochTime.GetIntDate(now).ToString(), ClaimValueTypes.Integer64) + }; + + // Roles + claims.AddRange(roleNames.Select(r => new Claim(ClaimTypes.Role, r))); + // Permisos + claims.AddRange(perms.Select(p => new Claim("perm", p))); + + var token = new JwtSecurityToken( + issuer: jwtSettings["Issuer"], + audience: jwtSettings["Audience"], + claims: claims, + notBefore: now, + expires: now.AddMinutes(60), + signingCredentials: creds); + + var jwt = new JwtSecurityTokenHandler().WriteToken(token); + + var session = new SessionInfo( + user.Id, + user.UserName ?? string.Empty, + [.. roleNames], + perms); + + return (new TokenResponse(jwt, token.ValidTo), session); + } +} diff --git a/src/OTManager.Api/Account/Services/Implement/SessionContext.cs b/src/OTManager.Api/Account/Services/Implement/SessionContext.cs new file mode 100644 index 0000000..3512b16 --- /dev/null +++ b/src/OTManager.Api/Account/Services/Implement/SessionContext.cs @@ -0,0 +1,44 @@ +using System.Security.Claims; + +using OTManager.Api.Account.Records; +using OTManager.Api.Account.Services.Interface; + +namespace OTManager.Api.Account.Services.Implement; + +public class SessionContext(IHttpContextAccessor http) : ISessionContext +{ + private const string PermissionClaimType = "perm"; + public SessionInfo? GetCurrent() + { + + var u = http.HttpContext?.User; + if (u == null || !(u.Identity?.IsAuthenticated ?? false)) return null; + + var sub = u.FindFirstValue(ClaimTypes.NameIdentifier) + ?? u.FindFirstValue("sub"); + if (!Guid.TryParse(sub, out var userId)) return null; + + + var userName = u.Identity?.Name + ?? u.FindFirstValue(ClaimTypes.Name) + ?? string.Empty; + + var roles = u.FindAll(ClaimTypes.Role) + .Select(x => x.Value).Distinct().ToArray(); + + var perms = u.FindAll(PermissionClaimType) + .Select(x => x.Value).Distinct().ToArray(); + + return new SessionInfo + ( + userId, + userName, + roles, + perms + ); + + } + + public string? GetCorrelationId() => http.HttpContext?.Items["CorrelationId"] as string; + +} diff --git a/src/OTManager.Api/Account/Services/Interface/IJwtTokenService.cs b/src/OTManager.Api/Account/Services/Interface/IJwtTokenService.cs new file mode 100644 index 0000000..2334d18 --- /dev/null +++ b/src/OTManager.Api/Account/Services/Interface/IJwtTokenService.cs @@ -0,0 +1,9 @@ +using OTManager.Api.Account.Records; +using OTManager.Data.Account; + +namespace OTManager.Api.Account.Services.Interface; + +public interface IJwtTokenService +{ + Task<(TokenResponse token, SessionInfo session)> CreateTokenAsync(ApplicationUser user); +} diff --git a/src/OTManager.Api/Account/Services/Interface/ISessionContext.cs b/src/OTManager.Api/Account/Services/Interface/ISessionContext.cs new file mode 100644 index 0000000..53cae2e --- /dev/null +++ b/src/OTManager.Api/Account/Services/Interface/ISessionContext.cs @@ -0,0 +1,9 @@ +using OTManager.Api.Account.Records; + +namespace OTManager.Api.Account.Services.Interface; + +public interface ISessionContext +{ + SessionInfo? GetCurrent(); + string? GetCorrelationId(); +} diff --git a/src/OTManager.Api/Controllers/MaterialController.cs b/src/OTManager.Api/Controllers/MaterialController.cs index 830163e..701a775 100644 --- a/src/OTManager.Api/Controllers/MaterialController.cs +++ b/src/OTManager.Api/Controllers/MaterialController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using OTManager.App.Dtos.Materials; @@ -8,6 +9,7 @@ namespace OTManager.Api.Controllers; [ApiController] [Route("api/[controller]")] +[Authorize] public class MaterialController : ControllerBase { private readonly IMaterialService _materialService; diff --git a/src/OTManager.Api/Controllers/ServiceOrderController.cs b/src/OTManager.Api/Controllers/ServiceOrderController.cs index 0e04376..0e6e880 100644 --- a/src/OTManager.Api/Controllers/ServiceOrderController.cs +++ b/src/OTManager.Api/Controllers/ServiceOrderController.cs @@ -1,3 +1,4 @@ +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using OTManager.App.Dtos.Orders; @@ -8,6 +9,7 @@ namespace OTManager.Api.Controllers; [ApiController] [Route("api/[controller]")] +[Authorize] public class ServiceOrderController : ControllerBase { private readonly IOrderServiceAppService _serviceOrderService; diff --git a/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs b/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs index d02f5a7..116fe8c 100644 --- a/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs +++ b/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs @@ -1,3 +1,7 @@ +using System.Threading.RateLimiting; + +using Microsoft.AspNetCore.RateLimiting; + using OTManager.App.Mappers; using OTManager.App.Services; using OTManager.Data.Context; @@ -10,6 +14,7 @@ public static class ApplicationDependencyInjection { public static IServiceCollection AddApplicationDependency(this IServiceCollection services, IConfiguration configuration) { + services.AddAppIdentityAuthentication(configuration); services.AddDbContextExtension(configuration); services.AddSwaggerExtension(configuration); services.AddRepositoryExtension(); @@ -17,6 +22,39 @@ public static IServiceCollection AddApplicationDependency(this IServiceCollectio services.AddControllers(); services.AddAppServices(); services.AddAppMappers(); + + services.AddRateLimiter(static _ => _ + .AddFixedWindowLimiter("fixed", opt => + { + opt.PermitLimit = 4; + opt.Window = TimeSpan.FromSeconds(12); + opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; + opt.QueueLimit = 2; + }) + .AddSlidingWindowLimiter("sliding", opt => + { + opt.PermitLimit = 4; + opt.Window = TimeSpan.FromSeconds(12); + opt.SegmentsPerWindow = 2; + opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; + opt.QueueLimit = 2; + }) + .AddTokenBucketLimiter("token", opt => + { + opt.TokenLimit = 4; + opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; + opt.QueueLimit = 4; + opt.ReplenishmentPeriod = TimeSpan.FromSeconds(5); + opt.TokensPerPeriod = 2; + opt.AutoReplenishment = true; + }) + .AddConcurrencyLimiter("concurrency", opt => + { + opt.PermitLimit = 5; + opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; + opt.QueueLimit = 3; + })); + return services; } } diff --git a/src/OTManager.Api/OTManager.Api.csproj b/src/OTManager.Api/OTManager.Api.csproj index 0932c0d..37c86d2 100644 --- a/src/OTManager.Api/OTManager.Api.csproj +++ b/src/OTManager.Api/OTManager.Api.csproj @@ -8,8 +8,9 @@ - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/OTManager.Api/Program.cs b/src/OTManager.Api/Program.cs index 899d14f..a692b50 100644 --- a/src/OTManager.Api/Program.cs +++ b/src/OTManager.Api/Program.cs @@ -17,6 +17,10 @@ app.UseHttpsRedirection(); +app.UseAuthentication(); + +app.UseAuthorization(); + app.MapControllers(); app.Run(); diff --git a/src/OTManager.App/Services/Implements/MaterialCostService.cs b/src/OTManager.App/Services/Implements/MaterialCostService.cs deleted file mode 100644 index 20e21d6..0000000 --- a/src/OTManager.App/Services/Implements/MaterialCostService.cs +++ /dev/null @@ -1,102 +0,0 @@ -using OTManager.App.Dtos.MaterialCosts; -using OTManager.App.Mappers.Interfaces; -using OTManager.App.Services.Interfaces; -using OTManager.Core.QueryParams; -using OTManager.Data.UoW; - -namespace OTManager.App.Services.Implements; - -public class MaterialCostService : IMaterialCostService -{ - private readonly IUnitOfWork _unitOfWork; - private readonly IMaterialCostMapper _mapper; - - public MaterialCostService(IUnitOfWork unitOfWork, IMaterialCostMapper mapper) - { - _unitOfWork = unitOfWork; - _mapper = mapper; - } - - public async Task> GetAllAsync() - { - var items = await _unitOfWork.MaterialCosts.GetAllAsync(); - return items.Select(_mapper.ToEntityDto); - } - - public async Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(MaterialCostQueryParams query) - { - var (items, total) = await _unitOfWork.MaterialCosts.GetFilteredAsync(query); - return (items.Select(_mapper.ToEntityDto), total); - } - - public async Task GetByIdAsync(Guid id) - { - var item = await _unitOfWork.MaterialCosts.GetByIdAsync(id); - return item is null ? null : _mapper.ToEntityDto(item); - } - - public async Task CreateAsync(WriteMaterialCostDto dto) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = _mapper.FromWriteDto(dto); - await _unitOfWork.MaterialCosts.AddAsync(entity); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return _mapper.ToEntityDto(entity); - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } - - public async Task UpdateAsync(Guid id, UpdateMaterialCostDto dto) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = await _unitOfWork.MaterialCosts.GetByIdAsync(id); - if (entity is null) - { - await _unitOfWork.RollbackTransactionAsync(); - return false; - } - _mapper.FromUpdateDto(entity, dto); - await _unitOfWork.MaterialCosts.UpdateAsync(entity); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return true; - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } - - public async Task DeleteAsync(Guid id) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = await _unitOfWork.MaterialCosts.GetByIdAsync(id); - if (entity is null) - { - await _unitOfWork.RollbackTransactionAsync(); - return false; - } - await _unitOfWork.MaterialCosts.DeleteAsync(id); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return true; - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } -} diff --git a/src/OTManager.App/Services/Implements/ServiceCostService.cs b/src/OTManager.App/Services/Implements/ServiceCostService.cs deleted file mode 100644 index 5afb6d5..0000000 --- a/src/OTManager.App/Services/Implements/ServiceCostService.cs +++ /dev/null @@ -1,102 +0,0 @@ -using OTManager.App.Dtos.ServiceCosts; -using OTManager.App.Mappers.Interfaces; -using OTManager.App.Services.Interfaces; -using OTManager.Core.QueryParams; -using OTManager.Data.UoW; - -namespace OTManager.App.Services.Implements; - -public class ServiceCostService : IServiceCostService -{ - private readonly IUnitOfWork _unitOfWork; - private readonly IServiceCostMapper _mapper; - - public ServiceCostService(IUnitOfWork unitOfWork, IServiceCostMapper mapper) - { - _unitOfWork = unitOfWork; - _mapper = mapper; - } - - public async Task> GetAllAsync() - { - var items = await _unitOfWork.ServiceCosts.GetAllAsync(); - return items.Select(_mapper.ToEntityDto); - } - - public async Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(ServiceCostQueryParams query) - { - var (items, total) = await _unitOfWork.ServiceCosts.GetFilteredAsync(query); - return (items.Select(_mapper.ToEntityDto), total); - } - - public async Task GetByIdAsync(Guid id) - { - var item = await _unitOfWork.ServiceCosts.GetByIdAsync(id); - return item is null ? null : _mapper.ToEntityDto(item); - } - - public async Task CreateAsync(WriteServiceCostDto dto) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = _mapper.FromWriteDto(dto); - await _unitOfWork.ServiceCosts.AddAsync(entity); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return _mapper.ToEntityDto(entity); - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } - - public async Task UpdateAsync(Guid id, UpdateServiceCostDto dto) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = await _unitOfWork.ServiceCosts.GetByIdAsync(id); - if (entity is null) - { - await _unitOfWork.RollbackTransactionAsync(); - return false; - } - _mapper.FromUpdateDto(entity, dto); - await _unitOfWork.ServiceCosts.UpdateAsync(entity); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return true; - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } - - public async Task DeleteAsync(Guid id) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = await _unitOfWork.ServiceCosts.GetByIdAsync(id); - if (entity is null) - { - await _unitOfWork.RollbackTransactionAsync(); - return false; - } - await _unitOfWork.ServiceCosts.DeleteAsync(id); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return true; - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } -} diff --git a/src/OTManager.App/Services/Implements/WorkerCostService.cs b/src/OTManager.App/Services/Implements/WorkerCostService.cs deleted file mode 100644 index 52dc795..0000000 --- a/src/OTManager.App/Services/Implements/WorkerCostService.cs +++ /dev/null @@ -1,102 +0,0 @@ -using OTManager.App.Dtos.WorkerCosts; -using OTManager.App.Mappers.Interfaces; -using OTManager.App.Services.Interfaces; -using OTManager.Core.QueryParams; -using OTManager.Data.UoW; - -namespace OTManager.App.Services.Implements; - -public class WorkerCostService : IWorkerCostService -{ - private readonly IUnitOfWork _unitOfWork; - private readonly IWorkerCostMapper _mapper; - - public WorkerCostService(IUnitOfWork unitOfWork, IWorkerCostMapper mapper) - { - _unitOfWork = unitOfWork; - _mapper = mapper; - } - - public async Task> GetAllAsync() - { - var items = await _unitOfWork.WorkerCosts.GetAllAsync(); - return items.Select(_mapper.ToEntityDto); - } - - public async Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(WorkerCostQueryParams query) - { - var (items, total) = await _unitOfWork.WorkerCosts.GetFilteredAsync(query); - return (items.Select(_mapper.ToEntityDto), total); - } - - public async Task GetByIdAsync(Guid id) - { - var item = await _unitOfWork.WorkerCosts.GetByIdAsync(id); - return item is null ? null : _mapper.ToEntityDto(item); - } - - public async Task CreateAsync(WriteWorkerCostDto dto) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = _mapper.FromWriteDto(dto); - await _unitOfWork.WorkerCosts.AddAsync(entity); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return _mapper.ToEntityDto(entity); - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } - - public async Task UpdateAsync(Guid id, UpdateWorkerCostDto dto) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = await _unitOfWork.WorkerCosts.GetByIdAsync(id); - if (entity is null) - { - await _unitOfWork.RollbackTransactionAsync(); - return false; - } - _mapper.FromUpdateDto(entity, dto); - await _unitOfWork.WorkerCosts.UpdateAsync(entity); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return true; - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } - - public async Task DeleteAsync(Guid id) - { - await _unitOfWork.BeginTransactionAsync(); - try - { - var entity = await _unitOfWork.WorkerCosts.GetByIdAsync(id); - if (entity is null) - { - await _unitOfWork.RollbackTransactionAsync(); - return false; - } - await _unitOfWork.WorkerCosts.DeleteAsync(id); - await _unitOfWork.SaveChangesAsync(); - await _unitOfWork.CommitTransactionAsync(); - return true; - } - catch - { - await _unitOfWork.RollbackTransactionAsync(); - throw; - } - } -} diff --git a/src/OTManager.App/Services/Interfaces/IMaterialCostService.cs b/src/OTManager.App/Services/Interfaces/IMaterialCostService.cs deleted file mode 100644 index 0cc945c..0000000 --- a/src/OTManager.App/Services/Interfaces/IMaterialCostService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using OTManager.App.Dtos.MaterialCosts; -using OTManager.Core.QueryParams; - -namespace OTManager.App.Services.Interfaces; - -public interface IMaterialCostService -{ - Task> GetAllAsync(); - Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(MaterialCostQueryParams query); - Task GetByIdAsync(Guid id); - Task CreateAsync(WriteMaterialCostDto dto); - Task UpdateAsync(Guid id, UpdateMaterialCostDto dto); - Task DeleteAsync(Guid id); -} diff --git a/src/OTManager.App/Services/Interfaces/IServiceCostService.cs b/src/OTManager.App/Services/Interfaces/IServiceCostService.cs deleted file mode 100644 index 02c84e4..0000000 --- a/src/OTManager.App/Services/Interfaces/IServiceCostService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using OTManager.App.Dtos.ServiceCosts; -using OTManager.Core.QueryParams; - -namespace OTManager.App.Services.Interfaces; - -public interface IServiceCostService -{ - Task> GetAllAsync(); - Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(ServiceCostQueryParams query); - Task GetByIdAsync(Guid id); - Task CreateAsync(WriteServiceCostDto dto); - Task UpdateAsync(Guid id, UpdateServiceCostDto dto); - Task DeleteAsync(Guid id); -} diff --git a/src/OTManager.App/Services/Interfaces/IWorkerCostService.cs b/src/OTManager.App/Services/Interfaces/IWorkerCostService.cs deleted file mode 100644 index 5bcc54e..0000000 --- a/src/OTManager.App/Services/Interfaces/IWorkerCostService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using OTManager.App.Dtos.WorkerCosts; -using OTManager.Core.QueryParams; - -namespace OTManager.App.Services.Interfaces; - -public interface IWorkerCostService -{ - Task> GetAllAsync(); - Task<(IEnumerable Items, int TotalCount)> GetFilteredAsync(WorkerCostQueryParams query); - Task GetByIdAsync(Guid id); - Task CreateAsync(WriteWorkerCostDto dto); - Task UpdateAsync(Guid id, UpdateWorkerCostDto dto); - Task DeleteAsync(Guid id); -} diff --git a/src/OTManager.App/Services/ServiceExtension.cs b/src/OTManager.App/Services/ServiceExtension.cs index e81efb9..da445ef 100644 --- a/src/OTManager.App/Services/ServiceExtension.cs +++ b/src/OTManager.App/Services/ServiceExtension.cs @@ -11,12 +11,9 @@ public static IServiceCollection AddAppServices(this IServiceCollection services { services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); - services.AddScoped(); services.AddScoped(); return services; diff --git a/src/OTManager.Core/Entities/Abstracts/Auditable.cs b/src/OTManager.Core/Entities/Abstracts/Auditable.cs index 3be8212..da1de2d 100644 --- a/src/OTManager.Core/Entities/Abstracts/Auditable.cs +++ b/src/OTManager.Core/Entities/Abstracts/Auditable.cs @@ -4,7 +4,7 @@ /// Interfaz para entidades auditables. /// /// Tipo de la clave primaria. - public interface IAuditableEntity + public interface IAuditableEntity where TKey : IEquatable { TKey Id { get; set; } DateTime? CreatedAt { get; set; } @@ -16,7 +16,7 @@ public interface IAuditableEntity // string? DeletedBy { get; set; } } - public class Auditable : IAuditableEntity + public class Auditable : IAuditableEntity where TKey : IEquatable { public required TKey Id { get; set; } public DateTime? CreatedAt { get; set; } diff --git a/src/OTManager.Core/Entities/Abstracts/IEntity.cs b/src/OTManager.Core/Entities/Abstracts/IEntity.cs index 27c5699..8a2d9a3 100644 --- a/src/OTManager.Core/Entities/Abstracts/IEntity.cs +++ b/src/OTManager.Core/Entities/Abstracts/IEntity.cs @@ -1,6 +1,6 @@ namespace OTManager.Core.Entities.Abstracts; -public interface IEntity +public interface IEntity where TKey : IEquatable { - T Id { get; set; } + TKey Id { get; set; } } diff --git a/src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs b/src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs new file mode 100644 index 0000000..7fc7b30 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs @@ -0,0 +1,8 @@ +namespace OTManager.Web.ClientServices.Authorize; + +public class AccountManagement(HttpClient httpClient) +{ + private readonly HttpClient _httpClient = httpClient; + + +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs new file mode 100644 index 0000000..725232d --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs @@ -0,0 +1,72 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Microsoft.AspNetCore.Components.Authorization; + +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService +{ + public class ApiAuthenticationStateProvider : AuthenticationStateProvider, IAuthStateNotifier + { + private readonly IAuthTokenProvider _tokenProvider; + + public ApiAuthenticationStateProvider(IAuthTokenProvider tokenProvider) + { + _tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); + } + + public override async Task GetAuthenticationStateAsync() + { + string? token = null; + try + { + token = await _tokenProvider.GetTokenAsync(); + } + catch + { + // treat errors as anonymous + } + + if (string.IsNullOrWhiteSpace(token)) + { + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + } + + try + { + var handler = new JwtSecurityTokenHandler(); + var jwt = handler.ReadJwtToken(token); + var claims = jwt.Claims.ToList(); + var identity = new ClaimsIdentity(claims, "jwt"); + var user = new ClaimsPrincipal(identity); + return new AuthenticationState(user); + } + catch + { + return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); + } + } + + public void NotifyUserAuthentication(string token) + { + if (string.IsNullOrWhiteSpace(token)) return; + + try + { + var handler = new JwtSecurityTokenHandler(); + var jwt = handler.ReadJwtToken(token); + var claims = jwt.Claims.ToList(); + var identity = new ClaimsIdentity(claims, "jwt"); + var user = new ClaimsPrincipal(identity); + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user))); + } + catch + { + // ignore invalid token + } + } + + public void NotifyUserLogout() + { + NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())))); + } + } +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs new file mode 100644 index 0000000..c353339 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs @@ -0,0 +1,118 @@ +using System.Net.Http.Json; +using OTManager.Web.ClientServices.DTOs.Identity; +using Microsoft.AspNetCore.Components.Authorization; +using System.Net.Http; + +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService; + +public class AuthService : IAuthService +{ + private readonly HttpClient _http; + private readonly IAuthTokenProvider _tokenProvider; + private readonly IAuthStateNotifier? _notifier; + + private const string ApiClientName = "ApiClient"; + + public AuthService(IHttpClientFactory httpFactory, IAuthTokenProvider tokenProvider, AuthenticationStateProvider authStateProvider) + { + if (httpFactory == null) throw new ArgumentNullException(nameof(httpFactory)); + _http = httpFactory.CreateClient(ApiClientName); + _tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); + _notifier = authStateProvider as IAuthStateNotifier; + } + + public async Task GetTokenAsync() + { + try + { + var token = await _tokenProvider.GetTokenAsync(); + return token ?? string.Empty; + } + catch + { + return string.Empty; + } + } + + public async Task LoginAsync(LoginRequest request) + { + if (request == null) throw new ArgumentNullException(nameof(request)); + + var res = await _http.PostAsJsonAsync("session/login", new { Username = request.Username, Password = request.Password }); + if (!res.IsSuccessStatusCode) + { + var msg = await res.Content.ReadAsStringAsync(); + return new LoginResponse(false, null, msg); + } + + var sessionResp = await res.Content.ReadFromJsonAsync(); + if (sessionResp is null || !sessionResp.Success || sessionResp.Data?.Token is null) + { + return new LoginResponse(false, null, sessionResp?.Message ?? "Invalid response"); + } + + var token = sessionResp.Data.Token; + await _tokenProvider.SetTokenAsync(token); + + try + { + _notifier?.NotifyUserAuthentication(token!); + } + catch + { + // ignore notifier errors + } + + return new LoginResponse(true, token, sessionResp.Message); + } + + public async Task LogOutAsync() + { + try + { + await _http.PostAsync("session/logout", null); + } + catch + { + // ignore logout errors + } + + try + { + await _tokenProvider.SetTokenAsync(null); + } + catch + { + // ignore token provider errors + } + + try + { + _notifier?.NotifyUserLogout(); + } + catch + { + // ignore notifier errors + } + } + + public async Task RegisterAsync(RegisterRequest request) + { + if (request == null) throw new ArgumentNullException(nameof(request)); + var res = await _http.PostAsJsonAsync("session/register", request); + return res.IsSuccessStatusCode; + } + + // Internal types to map API generic response + private class SessionWrapper + { + public bool Success { get; set; } + public string? Message { get; set; } + public TokenData? Data { get; set; } + } + private class TokenData + { + public string? Token { get; set; } + public object? SessionInfo { get; set; } + } +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs new file mode 100644 index 0000000..322337b --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs @@ -0,0 +1,28 @@ +using System.Net.Http.Headers; + +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService; + +public class AuthorizationMessageHandler : DelegatingHandler +{ + private readonly IAuthTokenProvider _tokenProvider; + + public AuthorizationMessageHandler(IAuthTokenProvider tokenProvider) + { + _tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); + } + + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + try + { + var token = await _tokenProvider.GetTokenAsync(); + if (!string.IsNullOrWhiteSpace(token)) request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + } + catch + { + // ignore token provider issues + } + + return await base.SendAsync(request, cancellationToken); + } +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs new file mode 100644 index 0000000..1519614 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs @@ -0,0 +1,64 @@ +using Microsoft.JSInterop; + +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService +{ + public class CookieAuthTokenProvider : IAuthTokenProvider + { + private readonly IJSRuntime _js; + private const string TokenKey = "token"; + private const int DefaultMaxAgeSeconds = 60 * 60; // 1 hour + + public CookieAuthTokenProvider(IJSRuntime js) + { + _js = js ?? throw new ArgumentNullException(nameof(js)); + } + + public async ValueTask GetTokenAsync() + { + try + { + // Read document.cookie via JS; returns string like "a=1; b=2" + var cookie = await _js.InvokeAsync("eval", "document.cookie"); + if (string.IsNullOrWhiteSpace(cookie)) return null; + + var parts = cookie.Split(';'); + foreach (var part in parts) + { + var trimmed = part.Trim(); + if (trimmed.StartsWith(TokenKey + "=", StringComparison.OrdinalIgnoreCase)) + { + return Uri.UnescapeDataString(trimmed.Substring(TokenKey.Length + 1)); + } + } + + return null; + } + catch + { + return null; + } + } + + public async ValueTask SetTokenAsync(string? token) + { + try + { + if (string.IsNullOrWhiteSpace(token)) + { + // Delete cookie + await _js.InvokeVoidAsync("eval", $"document.cookie = '{TokenKey}=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT' "); + } + else + { + var safe = Uri.EscapeDataString(token); + // Use secure, samesite=strict and a default max-age + await _js.InvokeVoidAsync("eval", $"document.cookie = '{TokenKey}={safe}; path=/; max-age={DefaultMaxAgeSeconds}; samesite=strict' "); + } + } + catch + { + // ignore JS errors + } + } + } +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs new file mode 100644 index 0000000..c74c9c5 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs @@ -0,0 +1,11 @@ +using OTManager.Web.ClientServices.DTOs.Identity; + +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService; + +public interface IAuthService +{ + Task RegisterAsync(RegisterRequest request); + Task LoginAsync(LoginRequest request); + Task LogOutAsync(); + Task GetTokenAsync(); +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs new file mode 100644 index 0000000..561d31e --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs @@ -0,0 +1,8 @@ +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService +{ + public interface IAuthStateNotifier + { + void NotifyUserAuthentication(string token); + void NotifyUserLogout(); + } +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs new file mode 100644 index 0000000..eb14255 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs @@ -0,0 +1,8 @@ +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService +{ + public interface IAuthTokenProvider + { + ValueTask GetTokenAsync(); + ValueTask SetTokenAsync(string? token); + } +} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs new file mode 100644 index 0000000..0fcbd8d --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs @@ -0,0 +1,46 @@ +using Microsoft.JSInterop; + +namespace OTManager.Web.ClientServices.Authorize.AuthenticationService +{ + public class JSAuthTokenProvider : IAuthTokenProvider + { + private readonly IJSRuntime _js; + private const string TokenKey = "token"; + + public JSAuthTokenProvider(IJSRuntime js) + { + _js = js ?? throw new ArgumentNullException(nameof(js)); + } + + public async ValueTask GetTokenAsync() + { + try + { + return await _js.InvokeAsync("localStorage.getItem", TokenKey); + } + catch + { + return null; + } + } + + public async ValueTask SetTokenAsync(string? token) + { + try + { + if (string.IsNullOrWhiteSpace(token)) + { + await _js.InvokeVoidAsync("localStorage.removeItem", TokenKey); + } + else + { + await _js.InvokeVoidAsync("localStorage.setItem", TokenKey, token); + } + } + catch + { + // ignore errors from JS + } + } + } +} diff --git a/src/OTManager.Web/ClientServices/Authorize/UserService.cs b/src/OTManager.Web/ClientServices/Authorize/UserService.cs new file mode 100644 index 0000000..34774d8 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Authorize/UserService.cs @@ -0,0 +1,14 @@ +using OTManager.Web.ClientServices.DTOs.Identity; + +namespace OTManager.Web.ClientServices.Authorize; + +public class UserService(HttpClient httpClient) +{ + private readonly HttpClient _httpClient = httpClient; + + public async Task> GetAll() + { + var users = await _httpClient.GetFromJsonAsync>("user"); + return users!.AsQueryable(); + } +} diff --git a/src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs b/src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs new file mode 100644 index 0000000..135032d --- /dev/null +++ b/src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs @@ -0,0 +1,20 @@ +using OTManager.Web.ClientServices.DTOs.Identity; + +namespace OTManager.Web.ClientServices.DTOs.Enterprises; + +public record EnterpriseReadDto +{ + public string Id { get; set; } = default!; + public string EnterpriseName { get; set; } = default!; + public string Description { get; set; } = default!; + + public IEnumerable Users { get; set; } = []; + + public DateTime? CreatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } + public DateTime? DeletedAt { get; set; } + + public bool IsActive { get; set; } + public bool IsSortable { get; set; } + public bool IsDeleted { get; set; } +} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs new file mode 100644 index 0000000..01e1ae7 --- /dev/null +++ b/src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs @@ -0,0 +1,6 @@ +namespace OTManager.Web.ClientServices.DTOs.Identity +{ + public class JwtSettings + { + } +} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs new file mode 100644 index 0000000..e96123e --- /dev/null +++ b/src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs @@ -0,0 +1,4 @@ +namespace OTManager.Web.ClientServices.DTOs.Identity +{ + public record LoginRequest(string Username, string Password); +} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs new file mode 100644 index 0000000..0b7ae74 --- /dev/null +++ b/src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs @@ -0,0 +1,9 @@ +namespace OTManager.Web.ClientServices.DTOs.Identity +{ + public record LoginResponse + ( + bool Success, + string? Token, + string? Message + ); +} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs new file mode 100644 index 0000000..6d57f3e --- /dev/null +++ b/src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs @@ -0,0 +1,4 @@ +namespace OTManager.Web.ClientServices.DTOs.Identity; + +public record RegisterRequest +(string Email, string Password); diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs new file mode 100644 index 0000000..7c71ef5 --- /dev/null +++ b/src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs @@ -0,0 +1,9 @@ +namespace OTManager.Web.ClientServices.DTOs.Identity; + +public record UserReadDto +{ + public string Id { get; set; } = string.Empty; + public string UserName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public IList Role { get; set; } = new List(); +} diff --git a/src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor b/src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor new file mode 100644 index 0000000..9ed3fb0 --- /dev/null +++ b/src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor @@ -0,0 +1,5 @@ +

AccountManagementPage

+ +@code { + +} diff --git a/src/OTManager.Web/Components/Account/Pages/Auth/Login.razor b/src/OTManager.Web/Components/Account/Pages/Auth/Login.razor new file mode 100644 index 0000000..186b5bb --- /dev/null +++ b/src/OTManager.Web/Components/Account/Pages/Auth/Login.razor @@ -0,0 +1,36 @@ +@page "/login" +@using Microsoft.AspNetCore.Components.Authorization +@inject OTManager.Web.ClientServices.Authorize.AuthenticationService.IAuthService AuthService +@inject NavigationManager Nav +@inject AuthenticationStateProvider AuthStateProvider + +

Login

+ + + + +

@message

+ +@code { + private string username = string.Empty; + private string password = string.Empty; + private string message = string.Empty; + + private async Task HandleLogin() + { + var res = await AuthService.LoginAsync(new OTManager.Web.ClientServices.DTOs.Identity.LoginRequest(username, password)); + if (res is null || !res.Success) + { + message = res?.Message ?? "Error en login"; + return; + } + + // Notify provider in case not already done + if (AuthStateProvider is OTManager.Web.ClientServices.Authorize.AuthenticationService.ApiAuthenticationStateProvider provider) + { + provider.NotifyUserAuthentication(res.Token!); + } + + Nav.NavigateTo("/"); + } +} diff --git a/src/OTManager.Web/Components/Account/Pages/Auth/Register.razor b/src/OTManager.Web/Components/Account/Pages/Auth/Register.razor new file mode 100644 index 0000000..f5b0834 --- /dev/null +++ b/src/OTManager.Web/Components/Account/Pages/Auth/Register.razor @@ -0,0 +1,29 @@ +@page "/register" +@inject OTManager.Web.ClientServices.Authorize.AuthenticationService.IAuthService AuthService +@inject NavigationManager Nav + +

Register

+ + + + +

@message

+ +@code { + private string email = string.Empty; + private string password = string.Empty; + private string message = string.Empty; + + private async Task HandleRegister() + { + var ok = await AuthService.RegisterAsync(new OTManager.Web.ClientServices.DTOs.Identity.RegisterRequest(email, password)); + if (ok) + { + Nav.NavigateTo("/login"); + } + else + { + message = "Error registrando usuario"; + } + } +} diff --git a/src/OTManager.Web/Components/Account/Pages/User/Profile.razor b/src/OTManager.Web/Components/Account/Pages/User/Profile.razor new file mode 100644 index 0000000..f5dd061 --- /dev/null +++ b/src/OTManager.Web/Components/Account/Pages/User/Profile.razor @@ -0,0 +1,37 @@ +@page "/profile" +@using Microsoft.AspNetCore.Components.Authorization +@using System.Security.Claims +@inject AuthenticationStateProvider AuthStateProvider + + + +

Perfil de Usuario

+

Username: @userName

+

Email: @email

+

Roles: @string.Join(", ", roles)

+
+ +

No autorizado. Login

+
+
+ +@code { + private string userName = string.Empty; + private string email = string.Empty; + private List roles = new(); + + protected override async Task OnInitializedAsync() + { + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + var user = authState.User; + if (user.Identity?.IsAuthenticated == true) + { + userName = user.Identity.Name ?? user.Claims.FirstOrDefault(c => c.Type == "unique_name")?.Value ?? string.Empty; + email = user.Claims.FirstOrDefault(c => c.Type == "email")?.Value ?? string.Empty; + roles = user.Claims + .Where(c => c.Type == "role" || c.Type == "roles" || c.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role") + .Select(c => c.Value) + .ToList(); + } + } +} diff --git a/src/OTManager.Web/Components/App.razor b/src/OTManager.Web/Components/App.razor index 7774499..7cda35b 100644 --- a/src/OTManager.Web/Components/App.razor +++ b/src/OTManager.Web/Components/App.razor @@ -14,7 +14,7 @@ - + diff --git a/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor b/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor index 4c012b0..7f15666 100644 --- a/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor +++ b/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor @@ -1 +1,53 @@ -

ClientListViewComponent

+ + + + + + + + + + + @Content + + + @Username | @LastModification | @Status + + + + + + + + + + + + +@code { + [Parameter] + public string Content { get; set; } = string.Empty; + [Parameter] + public string Username { get; set; } = string.Empty; + [Parameter] + public string Status { get; set; } = string.Empty; + [Parameter] + public DateTime LastModification { get; set; } + [Parameter] + public EventCallback OnOverseeEvent { get; set; } + [Parameter] + public EventCallback OnEditEvent { get; set; } + [Parameter] + public EventCallback OnDeleteEvent { get; set; } +} diff --git a/src/OTManager.Web/Components/Clients/Components/ContextMenuComponent.razor b/src/OTManager.Web/Components/Clients/Components/ContextMenuComponent.razor new file mode 100644 index 0000000..2530854 --- /dev/null +++ b/src/OTManager.Web/Components/Clients/Components/ContextMenuComponent.razor @@ -0,0 +1,46 @@ + + + + + + + Oversee + + + + + + + + Edite + + + + + + + + + Delete + + + + + +@code{ + [Parameter] + public EventCallback OnOverseeEvent { get; set; } + [Parameter] + public EventCallback OnEditEvent { get; set; } + [Parameter] + public EventCallback OnDeleteEvent { get; set; } +} \ No newline at end of file diff --git a/src/OTManager.Web/Components/Clients/Components/OverseeMenuComponent.razor b/src/OTManager.Web/Components/Clients/Components/OverseeMenuComponent.razor new file mode 100644 index 0000000..c5b4012 --- /dev/null +++ b/src/OTManager.Web/Components/Clients/Components/OverseeMenuComponent.razor @@ -0,0 +1,8 @@ + + @ChildContent + + +@code{ + [Parameter] + public RenderFragment? ChildContent { get; set; } +} \ No newline at end of file diff --git a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor index ce2daeb..4f5fc4f 100644 --- a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor +++ b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor @@ -2,4 +2,95 @@ Clients | Index - \ No newline at end of file + + + + Clients + + + + + + @if (OverseeItems is null) + { + Noting to see here + } + else + { + foreach (Item item in OverseeItems) + { + + + @item.Code + @item.Name + @item.UnitCost + + + } + } + + +@* + @if (OverseeItems is null) + { + Noting to see here + } + else + { + for (int i = 0; i < OverseeItems.Count; i++) + { + + + @OverseeItems[i].Code + @OverseeItems[i].Name + @OverseeItems[i].UnitCost + X + + + } + } + *@ + + + + + + + @if (Items is null) + { + + + + } + else + { + foreach (Item item in Items) + { + + + + + + } + } + diff --git a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs new file mode 100644 index 0000000..a790eff --- /dev/null +++ b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs @@ -0,0 +1,75 @@ +using System.Collections.ObjectModel; + +using Microsoft.FluentUI.AspNetCore.Components; + +namespace OTManager.Web.Components.Clients.Pages; + +public partial class ClientsIndex +{ + private IQueryable? Items { get; set; } + private ObservableCollection OverseeItems { get; set; } = []; + private int SelectedIndex = 0; + + private Item? NewItem { get; set; } + + protected override async Task OnInitializedAsync() + { + await Task.Delay(500); + Items = GetDemoItems(); + OverseeItems.CollectionChanged += (_, __) + => InvokeAsync(StateHasChanged); + } + + public void OverseeClient(Item item) + { + OverseeItems.Add(item); + } + + public void StopOverseeByIndex(int index) + { + if (index < 0 || index >= OverseeItems.Count) return; + var wasSelected = SelectedIndex == index; + OverseeItems.RemoveAt(index); + + if (OverseeItems.Count == 0) { + SelectedIndex = -1; + InvokeAsync(StateHasChanged); + return; + } + + if (wasSelected) + { + SelectedIndex = Math.Min(index, OverseeItems.Count - 1); + } + else if (index < SelectedIndex) + { + SelectedIndex--; + } + + InvokeAsync(StateHasChanged); + } + + public void HandleTabClose(FluentTab tab) + { + var client = (Item)tab.Data!; + var index = OverseeItems.IndexOf(client); + if (index == -1) return; + StopOverseeByIndex(index); + } + + public static IQueryable GetDemoItems() => new List{ + new ("001", "Client 1", "U", 13.5m ), + new ("002", "Client 2", "U", 13.5m), + new ("003", "Client 3", "U", 13.5m), + new ("004", "Client 4", "U", 13.5m), + new ("005", "Client 5", "U", 13.5m), + new ("006", "Client 6", "U", 13.5m) + }.AsQueryable(); + + public record Item( + string Code, + string Name, + string MeasureUnit, + decimal UnitCost + ); +} diff --git a/src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor b/src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor index a717b92..8194c7a 100644 --- a/src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor +++ b/src/OTManager.Web/Components/Dashboards/Pages/DashboardIndex.razor @@ -1,7 +1,57 @@ @page "/Home" +@using OTManager.Web.Components.Settings.Resources.Enums -

DashboardIndex

+Dashboard -@code { + + + + + + BlaBla + + + BlaBla + + + + + + + + + + + + + + zfdsgsrgergergergaergaergerg + + + zfdsgsrgergergergaergaergerg + + + + + + + zfdsgsrgergergergaergaergerg 1 + + + + + zfdsgsrgergergergaergaergerg 2 + + + + + +@code { + public LocalizerSelect? localizer { get; set; } } diff --git a/src/OTManager.Web/Components/Layout/MainLayout.razor b/src/OTManager.Web/Components/Layout/MainLayout.razor index 9902076..074cbd4 100644 --- a/src/OTManager.Web/Components/Layout/MainLayout.razor +++ b/src/OTManager.Web/Components/Layout/MainLayout.razor @@ -18,6 +18,10 @@ @Body + + + + Documentation and demos @@ -31,3 +35,8 @@ Reload 🗙 + +@code { + // Ensure this layout is accessible globally + public static Type LayoutType => typeof(MainLayout); +} diff --git a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs index fef4282..0275bc6 100644 --- a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs +++ b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs @@ -11,12 +11,12 @@ protected override async Task OnInitializedAsync() } public static IQueryable GetDemoItems() => new List{ - new ("002", "Item 2", "U", 13.5m ), - new ("002", "Item 2", "U", 13.5m), - new ("002", "Item 2", "U", 13.5m), - new ("002", "Item 2", "U", 13.5m), - new ("002", "Item 2", "U", 13.5m), - new ("002", "Item 2", "U", 13.5m) + new ("001", "Material 1", "U", 13.5m ), + new ("002", "Material 2", "U", 13.5m), + new ("003", "Material 3", "U", 13.5m), + new ("004", "Material 4", "U", 13.5m), + new ("005", "Material 5", "U", 13.5m), + new ("006", "Material 6", "U", 13.5m) }.AsQueryable(); public record Item( diff --git a/src/OTManager.Web/Components/Pages/Home.razor b/src/OTManager.Web/Components/Pages/Home.razor index 96714a2..db63207 100644 --- a/src/OTManager.Web/Components/Pages/Home.razor +++ b/src/OTManager.Web/Components/Pages/Home.razor @@ -1,7 +1,27 @@ @page "/" +@* @inject HttpClient http *@ + Home

Hello, world!

-Welcome to your new Fluent Blazor app. \ No newline at end of file +Welcome to your new Fluent Blazor app. + +@* @if (markdown == null) +{ +

cargando

+} +else +{ + +} + +@code{ + private string? markdown; + + protected override async Task OnInitializedAsync() + { + markdown = await http.GetStringAsync("Docs/README.md"); + } +} *@ \ No newline at end of file diff --git a/src/OTManager.Web/Components/Routes.razor b/src/OTManager.Web/Components/Routes.razor index f756e19..646d973 100644 --- a/src/OTManager.Web/Components/Routes.razor +++ b/src/OTManager.Web/Components/Routes.razor @@ -1,6 +1,15 @@ - - - - - - +@using Microsoft.AspNetCore.Components.Authorization + + + + + + + + + +

Sorry, there's nothing at this address.

+
+
+
+
diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor index 36cdb49..3fdfe15 100644 --- a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor +++ b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor @@ -1,6 +1,8 @@ @page "/settings/users" +@using OTManager.Web.ClientServices.Authorize @layout SettingsLayout +@inject UserService uService ? users; + public IQueryable? users; protected override async Task OnInitializedAsync() { // Simulate asynchronous loading to demonstrate a loading indicator await Task.Delay(500); - GetAll(); + await GetAllAsync(); } - private void GetAll() + private async Task GetAllAsync() { - //var response = await _user.Users.ToListAsync(); - //if (response is not null) users = response.AsQueryable(); + await uService.GetAll(); + + var response = await uService.GetAll(); + if (response is not null) + { + users = await uService.GetAll(); + } + else + { + users = null; + } } } diff --git a/src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs b/src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs new file mode 100644 index 0000000..4cf2e16 --- /dev/null +++ b/src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs @@ -0,0 +1,34 @@ +using System.Net; + +using Microsoft.FluentUI.AspNetCore.Components; + +namespace OTManager.Web.Extensions; + +public static class ApplicationDependencyInjection +{ + public static IServiceCollection AddApplicationDependency(this IServiceCollection services, IConfiguration configuration) + { + services.AddRazorComponents() + .AddInteractiveServerComponents(); + services.AddFluentUIComponents(); + + services.AddLocalization(); + services.AddControllers(); + + return services; + } + + public static IApplicationBuilder AddLocalizationBuilder(this IApplicationBuilder app) + { + string[] supportedCultures = ["es-ES", "en-US"]; + + var localizationOptions = new RequestLocalizationOptions() + .SetDefaultCulture(supportedCultures[0]) + .AddSupportedCultures(supportedCultures) + .AddSupportedUICultures(supportedCultures); + + app.UseRequestLocalization(localizationOptions); + + return app; + } +} diff --git a/src/OTManager.Web/OTManager.Web.csproj b/src/OTManager.Web/OTManager.Web.csproj index 8e5dfe2..666396f 100644 --- a/src/OTManager.Web/OTManager.Web.csproj +++ b/src/OTManager.Web/OTManager.Web.csproj @@ -7,7 +7,8 @@ - - + + + diff --git a/src/OTManager.Web/Program.cs b/src/OTManager.Web/Program.cs index 94dc547..a3f2f9b 100644 --- a/src/OTManager.Web/Program.cs +++ b/src/OTManager.Web/Program.cs @@ -1,27 +1,47 @@ -using Microsoft.FluentUI.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using OTManager.Web.ClientServices.Authorize; +using OTManager.Web.ClientServices.Authorize.AuthenticationService; using OTManager.Web.Components; +using OTManager.Web.Extensions; var builder = WebApplication.CreateBuilder(args); // Add services to the container. -builder.Services.AddRazorComponents() - .AddInteractiveServerComponents(); -builder.Services.AddFluentUIComponents(); -builder.Services.AddLocalization(); -builder.Services.AddControllers(); +ApplicationDependencyInjection.AddApplicationDependency(builder.Services, builder.Configuration); -var app = builder.Build(); +builder.Services.AddScoped(); +// Use cookie-based token storage in the browser via JS +builder.Services.AddScoped(); +// Authorization message handler depends on IAuthTokenProvider +builder.Services.AddTransient(); +// Register the authentication state provider and the notifier interface +builder.Services.AddScoped(); +builder.Services.AddScoped(sp => sp.GetRequiredService()); +builder.Services.AddScoped(sp => sp.GetRequiredService()); + +// Configure named HttpClient with AuthorizationMessageHandler +builder.Services.AddHttpClient("ApiClient", client => +{ + client.BaseAddress = new Uri("https://localhost:7257/api/"); +}) +.AddHttpMessageHandler(); + +// Register UserService to use the named client "ApiClient" +builder.Services.AddScoped(sp => +{ + var factory = sp.GetRequiredService(); + var client = factory.CreateClient("ApiClient"); + return new UserService(client); +}); -string[] supportedCultures = ["es-ES", "en-US"]; +builder.Services.AddOptions(); +builder.Services.AddAuthorizationCore(); // Use AddAuthorizationCore for Blazor -var localizationOptions = new RequestLocalizationOptions() - .SetDefaultCulture(supportedCultures[0]) - .AddSupportedCultures(supportedCultures) - .AddSupportedUICultures(supportedCultures); +var app = builder.Build(); -app.UseRequestLocalization(localizationOptions); +app.AddLocalizationBuilder(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) @@ -39,6 +59,10 @@ app.MapRazorComponents() .AddInteractiveServerRenderMode(); +app.UseAuthentication(); + +app.UseAuthorization(); + app.MapControllers(); app.Run(); diff --git a/src/OTManager.Web/wwwroot/Docs/LICENSE.txt b/src/OTManager.Web/wwwroot/Docs/LICENSE.txt new file mode 100644 index 0000000..c7b086f --- /dev/null +++ b/src/OTManager.Web/wwwroot/Docs/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Adrian Mesa + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/OTManager.Web/wwwroot/Docs/README.md b/src/OTManager.Web/wwwroot/Docs/README.md new file mode 100644 index 0000000..56ac08a --- /dev/null +++ b/src/OTManager.Web/wwwroot/Docs/README.md @@ -0,0 +1,40 @@ +# OTManager.Wapi + +OTManager.Wapi es una solucin para la gestin de rdenes de trabajo, clientes, materiales, servicios y costos en entornos empresariales. El proyecto est desarrollado en .NET 9 y sigue una arquitectura limpia y modular. + +## Caractersticas principales + +- **Gestin de rdenes de Trabajo**: Creacin, actualizacin y seguimiento de rdenes de trabajo. +- **Gestin de Clientes**: Administracin de informacin de clientes y su historial de rdenes. +- **Control de Materiales y Servicios**: Registro y control de materiales y servicios asociados a cada orden. +- **Costos y Facturacin**: Clculo y gestin de costos de materiales, servicios y mano de obra, as como generacin de facturas. +- **Auditora**: Seguimiento de cambios y acciones realizadas sobre las entidades principales. +- **API RESTful**: Exposicin de endpoints seguros y documentados para integracin con otros sistemas. +- **Documentacin interactiva (Swagger)**: Acceso restringido a la documentacin interactiva en entornos de desarrollo. +- **Unit of Work y Repositorios**: Patrn Unit of Work y repositorios para una gestin eficiente y desacoplada de la persistencia. +- **Arquitectura modular**: Separacin clara entre capa de datos, lgica de negocio, API y aplicacin. + +## Patrones de diseo implementados + +- **Unit of Work**: Para la gestin de transacciones y persistencia de datos de forma atmica. +- **Repository**: Para el acceso desacoplado a la capa de datos. +- **DTO (Data Transfer Object)**: Para la transferencia de datos entre capas y la API. +- **Dependency Injection**: Para la inyeccin de dependencias y facilitar el testing y la extensibilidad. +- **Controller/Endpoint**: Para la exposicin de la lgica de negocio a travs de una API RESTful. +- **Mapper**: Para la conversin entre entidades de dominio y DTOs. +- **Service Layer**: Para la encapsulacin de la lgica de negocio y orquestacin de operaciones. +- **Auditable Entity**: Para el seguimiento de cambios y auditora en las entidades principales. + +## Estructura del proyecto + +- `OTManager.Data`: Acceso a datos, entidades y auditora. +- `OTManager.Core`: Entidades de dominio y contratos. +- `OTManager.App`: Lgica de negocio y servicios de aplicacin. +- `OTManager.Api`: API RESTful y documentacin Swagger. + +## Requisitos +- .NET 9 SDK +- SQL Server u otro proveedor compatible con Entity Framework Core + +## Licencia +Este proyecto est bajo la Licencia MIT. Consulta el archivo LICENSE.txt para ms detalles. \ No newline at end of file From 184745964f7413438bfcee5c2768945937cac403 Mon Sep 17 00:00:00 2001 From: Adrian Mesa Sacasas Date: Sun, 2 Nov 2025 13:07:45 -0500 Subject: [PATCH 3/4] =?UTF-8?q?Refactorizaci=C3=B3n=20y=20mejoras=20en=20l?= =?UTF-8?q?a=20estructura=20del=20proyecto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se han realizado cambios significativos en varios archivos del proyecto, incluyendo la adición de nuevos servicios y la modificación de la estructura de los archivos. - Eliminadas importaciones innecesarias en `ApplicationRole.cs`. - Actualizada la configuración del modelo en `ApplicationDbContext.cs`. - Cambiado el espacio de nombres en `IdentityExtension.cs`. - Comentadas anotaciones de autorización en `MaterialController.cs`. - Agregados nuevos proyectos en `OTManager.Wapi.sln`. - Modificada la configuración de servicios en `Program.cs`. - Actualizados varios archivos `.razor` para eliminar código obsoleto. - Añadidos nuevos archivos de configuración para mejorar la gestión. - Actualizadas dependencias en `OTManager.Web.csproj`. - Mejorada la presentación y funcionalidad de componentes UI. - Introducido `MaterialService` para la interacción con la API. - Modificados DTOs para reflejar la nueva estructura de datos. - Ajustados estilos CSS para mejorar la apariencia visual. - Añadidos nuevos archivos de JavaScript para funcionalidades adicionales. - Eliminadas clases y métodos obsoletos en la gestión de autenticación. --- OTManager.Data/Account/ApplicationRole.cs | 8 +- .../Context/ApplicationDbContext.cs | 9 +- .../20251015220442_AuthenticationCommit.cs | 3 +- OTManager.Wapi.sln | 23 ++++ dist/OTManager.AppHost/AppHost.cs | 9 ++ .../OTManager.AppHost.csproj | 22 +++ .../Properties/launchSettings.json | 29 ++++ .../appsettings.Development.json | 8 ++ dist/OTManager.AppHost/appsettings.json | 9 ++ dist/OTManager.ServiceDefaults/Extensions.cs | 127 ++++++++++++++++++ .../OTManager.ServiceDefaults.csproj | 22 +++ .../Account/Extensions/IdentityExtension.cs | 3 +- .../Controllers/MaterialController.cs | 21 ++- .../ApplicationDependencyInjection.cs | 1 + src/OTManager.Api/OTManager.Api.csproj | 1 + src/OTManager.Api/Program.cs | 6 +- .../Authorize/AccountManagement.cs | 8 -- .../ApiAuthenticationStateProvider.cs | 72 ---------- .../AuthenticationService/AuthService.cs | 118 ---------------- .../AuthorizationMessageHandler.cs | 28 ---- .../CookieAuthTokenProvider.cs | 64 --------- .../AuthenticationService/IAuthService.cs | 11 -- .../IAuthStateNotifier.cs | 8 -- .../IAuthTokenProvider.cs | 8 -- .../JSAuthTokenProvider.cs | 46 ------- .../ClientServices/Authorize/UserService.cs | 14 -- .../ClientServices/Clients/ClientService.cs | 6 + .../DTOs/Enterprises/EnterpriseReadDto.cs | 20 --- .../DTOs/Identity/JwtSettings.cs | 6 - .../DTOs/Identity/LoginRequest.cs | 4 - .../DTOs/Identity/LoginResponse.cs | 9 -- .../DTOs/Identity/RegisterRequest.cs | 4 - .../DTOs/Identity/UserReadDto.cs | 9 -- .../Materials/MaterialService.cs | 29 ++++ .../Materials/Records/MaterialReadDto.cs | 8 ++ .../Account/Pages/AccountManagementPage.razor | 5 - .../Components/Account/Pages/Auth/Login.razor | 34 ++--- .../Account/Pages/Auth/Register.razor | 28 ++-- .../Account/Pages/User/Profile.razor | 28 ++-- .../Components/ClientListViewComponent.razor | 6 +- .../Clients/Pages/ClientsIndex.razor | 68 +--------- .../Clients/Pages/ClientsIndex.razor.cs | 44 +----- .../Components/Clients/_Imports.razor | 3 +- .../MaterialListViewComponent.razor | 2 +- .../Materials/Layout/MaterialLayout.razor | 22 +++ .../Materials/Pages/StocksIndex.razor | 19 +-- .../Materials/Pages/StocksIndex.razor.cs | 52 ++++--- .../Components/Materials/_Imports.razor | 6 +- .../Settings/Layout/SettingsLayout.razor | 4 +- .../Settings/Pages/SettingUserPage.razor | 10 +- .../Settings/Pages/SettingUserPage.razor.cs | 43 +++--- .../Components/Settings/_Imports.razor | 1 + .../ApplicationDependencyInjection.cs | 4 +- src/OTManager.Web/OTManager.Web.csproj | 11 +- src/OTManager.Web/Program.cs | 31 ++--- src/OTManager.Web/wwwroot/app.css | 13 ++ src/OTManager.Web/wwwroot/css/RHStiles.css | 1 + .../OTManager.WebComp.csproj | 22 +++ .../Shared}/ContextMenuComponent.razor | 0 src/OTManager.WebComp/_Imports.razor | 4 + src/OTManager.WebComp/wwwroot/background.png | Bin 0 -> 378 bytes .../wwwroot/exampleJsInterop.js | 6 + 62 files changed, 540 insertions(+), 700 deletions(-) create mode 100644 dist/OTManager.AppHost/AppHost.cs create mode 100644 dist/OTManager.AppHost/OTManager.AppHost.csproj create mode 100644 dist/OTManager.AppHost/Properties/launchSettings.json create mode 100644 dist/OTManager.AppHost/appsettings.Development.json create mode 100644 dist/OTManager.AppHost/appsettings.json create mode 100644 dist/OTManager.ServiceDefaults/Extensions.cs create mode 100644 dist/OTManager.ServiceDefaults/OTManager.ServiceDefaults.csproj delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs delete mode 100644 src/OTManager.Web/ClientServices/Authorize/UserService.cs create mode 100644 src/OTManager.Web/ClientServices/Clients/ClientService.cs delete mode 100644 src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs delete mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs delete mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs delete mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs delete mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs delete mode 100644 src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs create mode 100644 src/OTManager.Web/ClientServices/Materials/MaterialService.cs create mode 100644 src/OTManager.Web/ClientServices/Materials/Records/MaterialReadDto.cs delete mode 100644 src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor create mode 100644 src/OTManager.Web/Components/Materials/Layout/MaterialLayout.razor create mode 100644 src/OTManager.Web/wwwroot/css/RHStiles.css create mode 100644 src/OTManager.WebComp/OTManager.WebComp.csproj rename src/{OTManager.Web/Components/Clients/Components => OTManager.WebComp/Shared}/ContextMenuComponent.razor (100%) create mode 100644 src/OTManager.WebComp/_Imports.razor create mode 100644 src/OTManager.WebComp/wwwroot/background.png create mode 100644 src/OTManager.WebComp/wwwroot/exampleJsInterop.js diff --git a/OTManager.Data/Account/ApplicationRole.cs b/OTManager.Data/Account/ApplicationRole.cs index 10e8046..55a7991 100644 --- a/OTManager.Data/Account/ApplicationRole.cs +++ b/OTManager.Data/Account/ApplicationRole.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity; namespace OTManager.Data.Account; diff --git a/OTManager.Data/Context/ApplicationDbContext.cs b/OTManager.Data/Context/ApplicationDbContext.cs index fb78ae2..a4f14d1 100644 --- a/OTManager.Data/Context/ApplicationDbContext.cs +++ b/OTManager.Data/Context/ApplicationDbContext.cs @@ -21,13 +21,16 @@ protected override void OnModelCreating(ModelBuilder builder) { builder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly); - builder.Entity>(e => { + builder.Entity>(e => + { e.HasKey(l => new { l.LoginProvider, l.ProviderKey }); }); - builder.Entity>(e => { + builder.Entity>(e => + { e.HasKey(r => new { r.UserId, r.RoleId }); }); - builder.Entity>(e => { + builder.Entity>(e => + { e.HasKey(t => new { t.UserId, t.LoginProvider, t.Name }); }); } diff --git a/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.cs b/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.cs index 0c3a0f6..9d6a96c 100644 --- a/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.cs +++ b/OTManager.Data/Migrations/20251015220442_AuthenticationCommit.cs @@ -1,5 +1,4 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/OTManager.Wapi.sln b/OTManager.Wapi.sln index 5f54849..35c2f51 100644 --- a/OTManager.Wapi.sln +++ b/OTManager.Wapi.sln @@ -21,6 +21,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{C5CFB38B-0E9 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OTManager.Web", "src\OTManager.Web\OTManager.Web.csproj", "{9F3A116A-1006-4770-81E4-D57CD3583D4C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OTManager.WebComp", "src\OTManager.WebComp\OTManager.WebComp.csproj", "{73746EFB-FC7D-4B7C-949B-CC2FDEB2D7F6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dist", "dist", "{0E9CFBD1-9DFF-4F8E-8A2B-C184A3E7A6D7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OTManager.AppHost", "dist\OTManager.AppHost\OTManager.AppHost.csproj", "{8D8DC712-1E28-47F9-809F-C7A58300ED83}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OTManager.ServiceDefaults", "dist\OTManager.ServiceDefaults\OTManager.ServiceDefaults.csproj", "{E799EFE6-12B5-7F10-CF22-5A9260D5D08C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -47,6 +55,18 @@ Global {9F3A116A-1006-4770-81E4-D57CD3583D4C}.Debug|Any CPU.Build.0 = Debug|Any CPU {9F3A116A-1006-4770-81E4-D57CD3583D4C}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F3A116A-1006-4770-81E4-D57CD3583D4C}.Release|Any CPU.Build.0 = Release|Any CPU + {73746EFB-FC7D-4B7C-949B-CC2FDEB2D7F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73746EFB-FC7D-4B7C-949B-CC2FDEB2D7F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73746EFB-FC7D-4B7C-949B-CC2FDEB2D7F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73746EFB-FC7D-4B7C-949B-CC2FDEB2D7F6}.Release|Any CPU.Build.0 = Release|Any CPU + {8D8DC712-1E28-47F9-809F-C7A58300ED83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D8DC712-1E28-47F9-809F-C7A58300ED83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D8DC712-1E28-47F9-809F-C7A58300ED83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D8DC712-1E28-47F9-809F-C7A58300ED83}.Release|Any CPU.Build.0 = Release|Any CPU + {E799EFE6-12B5-7F10-CF22-5A9260D5D08C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E799EFE6-12B5-7F10-CF22-5A9260D5D08C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E799EFE6-12B5-7F10-CF22-5A9260D5D08C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E799EFE6-12B5-7F10-CF22-5A9260D5D08C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -57,6 +77,9 @@ Global {4A32B4C2-DEF6-40BA-AE40-1DEF2D26D002} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {C450D449-89E9-43D4-A52D-A0F1DF8FE517} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {9F3A116A-1006-4770-81E4-D57CD3583D4C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {73746EFB-FC7D-4B7C-949B-CC2FDEB2D7F6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {8D8DC712-1E28-47F9-809F-C7A58300ED83} = {0E9CFBD1-9DFF-4F8E-8A2B-C184A3E7A6D7} + {E799EFE6-12B5-7F10-CF22-5A9260D5D08C} = {0E9CFBD1-9DFF-4F8E-8A2B-C184A3E7A6D7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8434D880-E4C5-4974-9EE1-67CD8E257B50} diff --git a/dist/OTManager.AppHost/AppHost.cs b/dist/OTManager.AppHost/AppHost.cs new file mode 100644 index 0000000..d656c21 --- /dev/null +++ b/dist/OTManager.AppHost/AppHost.cs @@ -0,0 +1,9 @@ +var builder = DistributedApplication.CreateBuilder(args); + +var api = builder.AddProject("ot-manager-api"); + +builder.AddProject("ot-manager-web") + .WithHttpEndpoint() + .WaitFor(api); + +builder.Build().Run(); diff --git a/dist/OTManager.AppHost/OTManager.AppHost.csproj b/dist/OTManager.AppHost/OTManager.AppHost.csproj new file mode 100644 index 0000000..8e8b655 --- /dev/null +++ b/dist/OTManager.AppHost/OTManager.AppHost.csproj @@ -0,0 +1,22 @@ + + + + + + Exe + net9.0 + enable + enable + 98426b1f-635d-4719-85eb-84f0805785c2 + + + + + + + + + + + + diff --git a/dist/OTManager.AppHost/Properties/launchSettings.json b/dist/OTManager.AppHost/Properties/launchSettings.json new file mode 100644 index 0000000..beaed5f --- /dev/null +++ b/dist/OTManager.AppHost/Properties/launchSettings.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:17254;http://localhost:15040", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21031", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22124" + } + }, + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:15040", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development", + "DOTNET_ENVIRONMENT": "Development", + "ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19040", + "ASPIRE_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20248" + } + } + } +} diff --git a/dist/OTManager.AppHost/appsettings.Development.json b/dist/OTManager.AppHost/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/dist/OTManager.AppHost/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/dist/OTManager.AppHost/appsettings.json b/dist/OTManager.AppHost/appsettings.json new file mode 100644 index 0000000..31c092a --- /dev/null +++ b/dist/OTManager.AppHost/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Aspire.Hosting.Dcp": "Warning" + } + } +} diff --git a/dist/OTManager.ServiceDefaults/Extensions.cs b/dist/OTManager.ServiceDefaults/Extensions.cs new file mode 100644 index 0000000..112c128 --- /dev/null +++ b/dist/OTManager.ServiceDefaults/Extensions.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Diagnostics.HealthChecks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.ServiceDiscovery; +using OpenTelemetry; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; + +namespace Microsoft.Extensions.Hosting; + +// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry. +// This project should be referenced by each service project in your solution. +// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults +public static class Extensions +{ + private const string HealthEndpointPath = "/health"; + private const string AlivenessEndpointPath = "/alive"; + + public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.ConfigureOpenTelemetry(); + + builder.AddDefaultHealthChecks(); + + builder.Services.AddServiceDiscovery(); + + builder.Services.ConfigureHttpClientDefaults(http => + { + // Turn on resilience by default + http.AddStandardResilienceHandler(); + + // Turn on service discovery by default + http.AddServiceDiscovery(); + }); + + // Uncomment the following to restrict the allowed schemes for service discovery. + // builder.Services.Configure(options => + // { + // options.AllowedSchemes = ["https"]; + // }); + + return builder; + } + + public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Logging.AddOpenTelemetry(logging => + { + logging.IncludeFormattedMessage = true; + logging.IncludeScopes = true; + }); + + builder.Services.AddOpenTelemetry() + .WithMetrics(metrics => + { + metrics.AddAspNetCoreInstrumentation() + .AddHttpClientInstrumentation() + .AddRuntimeInstrumentation(); + }) + .WithTracing(tracing => + { + tracing.AddSource(builder.Environment.ApplicationName) + .AddAspNetCoreInstrumentation(tracing => + // Exclude health check requests from tracing + tracing.Filter = context => + !context.Request.Path.StartsWithSegments(HealthEndpointPath) + && !context.Request.Path.StartsWithSegments(AlivenessEndpointPath) + ) + // Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package) + //.AddGrpcClientInstrumentation() + .AddHttpClientInstrumentation(); + }); + + builder.AddOpenTelemetryExporters(); + + return builder; + } + + private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]); + + if (useOtlpExporter) + { + builder.Services.AddOpenTelemetry().UseOtlpExporter(); + } + + // Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package) + //if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"])) + //{ + // builder.Services.AddOpenTelemetry() + // .UseAzureMonitor(); + //} + + return builder; + } + + public static TBuilder AddDefaultHealthChecks(this TBuilder builder) where TBuilder : IHostApplicationBuilder + { + builder.Services.AddHealthChecks() + // Add a default liveness check to ensure app is responsive + .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]); + + return builder; + } + + public static WebApplication MapDefaultEndpoints(this WebApplication app) + { + // Adding health checks endpoints to applications in non-development environments has security implications. + // See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments. + if (app.Environment.IsDevelopment()) + { + // All health checks must pass for app to be considered ready to accept traffic after starting + app.MapHealthChecks(HealthEndpointPath); + + // Only health checks tagged with the "live" tag must pass for app to be considered alive + app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions + { + Predicate = r => r.Tags.Contains("live") + }); + } + + return app; + } +} diff --git a/dist/OTManager.ServiceDefaults/OTManager.ServiceDefaults.csproj b/dist/OTManager.ServiceDefaults/OTManager.ServiceDefaults.csproj new file mode 100644 index 0000000..233173d --- /dev/null +++ b/dist/OTManager.ServiceDefaults/OTManager.ServiceDefaults.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + + + + + diff --git a/src/OTManager.Api/Account/Extensions/IdentityExtension.cs b/src/OTManager.Api/Account/Extensions/IdentityExtension.cs index 10571fe..00d28e2 100644 --- a/src/OTManager.Api/Account/Extensions/IdentityExtension.cs +++ b/src/OTManager.Api/Account/Extensions/IdentityExtension.cs @@ -2,7 +2,6 @@ using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.IdentityModel.Tokens; @@ -11,7 +10,7 @@ using OTManager.Data.Account; using OTManager.Data.Context; -namespace OTManager.Api.Extensions; +namespace OTManager.Api.Account.Extensions; public static class IdentityExtension { diff --git a/src/OTManager.Api/Controllers/MaterialController.cs b/src/OTManager.Api/Controllers/MaterialController.cs index 701a775..2ad7e84 100644 --- a/src/OTManager.Api/Controllers/MaterialController.cs +++ b/src/OTManager.Api/Controllers/MaterialController.cs @@ -1,4 +1,3 @@ -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using OTManager.App.Dtos.Materials; @@ -9,21 +8,17 @@ namespace OTManager.Api.Controllers; [ApiController] [Route("api/[controller]")] -[Authorize] -public class MaterialController : ControllerBase +//[Authorize] +public class MaterialController(IMaterialService materialService) : ControllerBase { - private readonly IMaterialService _materialService; + private readonly IMaterialService _materialService = materialService; - public MaterialController(IMaterialService materialService) + [HttpPost("filter")] + public async Task GetAll([FromBody] MaterialQueryParams query) { - _materialService = materialService; - } - - [HttpGet] - public async Task GetAll([FromQuery] MaterialQueryParams query) - { - var (Items, TotalCount) = await _materialService.GetFilteredAsync(query); - return Ok(new { Items, TotalCount }); + //var (Items, TotalCount) = await _materialService.GetFilteredAsync(query); + var Items = await _materialService.GetFilteredAsync(query); + return Ok(Items.Items); } [HttpGet("{id}")] diff --git a/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs b/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs index 116fe8c..650d1a9 100644 --- a/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs +++ b/src/OTManager.Api/Extensions/ApplicationDependencyInjection.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.RateLimiting; +using OTManager.Api.Account.Extensions; using OTManager.App.Mappers; using OTManager.App.Services; using OTManager.Data.Context; diff --git a/src/OTManager.Api/OTManager.Api.csproj b/src/OTManager.Api/OTManager.Api.csproj index 37c86d2..7c289ef 100644 --- a/src/OTManager.Api/OTManager.Api.csproj +++ b/src/OTManager.Api/OTManager.Api.csproj @@ -18,6 +18,7 @@
+ diff --git a/src/OTManager.Api/Program.cs b/src/OTManager.Api/Program.cs index a692b50..0494c74 100644 --- a/src/OTManager.Api/Program.cs +++ b/src/OTManager.Api/Program.cs @@ -2,11 +2,15 @@ var builder = WebApplication.CreateBuilder(args); -// Usar el nombre de clase esttico para evitar ambigedad +builder.AddServiceDefaults(); + +// Usar el nombre de clase estático para evitar ambigüedad ApplicationDependencyInjection.AddApplicationDependency(builder.Services, builder.Configuration); var app = builder.Build(); +app.MapDefaultEndpoints(); + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { diff --git a/src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs b/src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs deleted file mode 100644 index 7fc7b30..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AccountManagement.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OTManager.Web.ClientServices.Authorize; - -public class AccountManagement(HttpClient httpClient) -{ - private readonly HttpClient _httpClient = httpClient; - - -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs deleted file mode 100644 index 725232d..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/ApiAuthenticationStateProvider.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.IdentityModel.Tokens.Jwt; -using System.Security.Claims; -using Microsoft.AspNetCore.Components.Authorization; - -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService -{ - public class ApiAuthenticationStateProvider : AuthenticationStateProvider, IAuthStateNotifier - { - private readonly IAuthTokenProvider _tokenProvider; - - public ApiAuthenticationStateProvider(IAuthTokenProvider tokenProvider) - { - _tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); - } - - public override async Task GetAuthenticationStateAsync() - { - string? token = null; - try - { - token = await _tokenProvider.GetTokenAsync(); - } - catch - { - // treat errors as anonymous - } - - if (string.IsNullOrWhiteSpace(token)) - { - return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); - } - - try - { - var handler = new JwtSecurityTokenHandler(); - var jwt = handler.ReadJwtToken(token); - var claims = jwt.Claims.ToList(); - var identity = new ClaimsIdentity(claims, "jwt"); - var user = new ClaimsPrincipal(identity); - return new AuthenticationState(user); - } - catch - { - return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); - } - } - - public void NotifyUserAuthentication(string token) - { - if (string.IsNullOrWhiteSpace(token)) return; - - try - { - var handler = new JwtSecurityTokenHandler(); - var jwt = handler.ReadJwtToken(token); - var claims = jwt.Claims.ToList(); - var identity = new ClaimsIdentity(claims, "jwt"); - var user = new ClaimsPrincipal(identity); - NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user))); - } - catch - { - // ignore invalid token - } - } - - public void NotifyUserLogout() - { - NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())))); - } - } -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs deleted file mode 100644 index c353339..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthService.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System.Net.Http.Json; -using OTManager.Web.ClientServices.DTOs.Identity; -using Microsoft.AspNetCore.Components.Authorization; -using System.Net.Http; - -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService; - -public class AuthService : IAuthService -{ - private readonly HttpClient _http; - private readonly IAuthTokenProvider _tokenProvider; - private readonly IAuthStateNotifier? _notifier; - - private const string ApiClientName = "ApiClient"; - - public AuthService(IHttpClientFactory httpFactory, IAuthTokenProvider tokenProvider, AuthenticationStateProvider authStateProvider) - { - if (httpFactory == null) throw new ArgumentNullException(nameof(httpFactory)); - _http = httpFactory.CreateClient(ApiClientName); - _tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); - _notifier = authStateProvider as IAuthStateNotifier; - } - - public async Task GetTokenAsync() - { - try - { - var token = await _tokenProvider.GetTokenAsync(); - return token ?? string.Empty; - } - catch - { - return string.Empty; - } - } - - public async Task LoginAsync(LoginRequest request) - { - if (request == null) throw new ArgumentNullException(nameof(request)); - - var res = await _http.PostAsJsonAsync("session/login", new { Username = request.Username, Password = request.Password }); - if (!res.IsSuccessStatusCode) - { - var msg = await res.Content.ReadAsStringAsync(); - return new LoginResponse(false, null, msg); - } - - var sessionResp = await res.Content.ReadFromJsonAsync(); - if (sessionResp is null || !sessionResp.Success || sessionResp.Data?.Token is null) - { - return new LoginResponse(false, null, sessionResp?.Message ?? "Invalid response"); - } - - var token = sessionResp.Data.Token; - await _tokenProvider.SetTokenAsync(token); - - try - { - _notifier?.NotifyUserAuthentication(token!); - } - catch - { - // ignore notifier errors - } - - return new LoginResponse(true, token, sessionResp.Message); - } - - public async Task LogOutAsync() - { - try - { - await _http.PostAsync("session/logout", null); - } - catch - { - // ignore logout errors - } - - try - { - await _tokenProvider.SetTokenAsync(null); - } - catch - { - // ignore token provider errors - } - - try - { - _notifier?.NotifyUserLogout(); - } - catch - { - // ignore notifier errors - } - } - - public async Task RegisterAsync(RegisterRequest request) - { - if (request == null) throw new ArgumentNullException(nameof(request)); - var res = await _http.PostAsJsonAsync("session/register", request); - return res.IsSuccessStatusCode; - } - - // Internal types to map API generic response - private class SessionWrapper - { - public bool Success { get; set; } - public string? Message { get; set; } - public TokenData? Data { get; set; } - } - private class TokenData - { - public string? Token { get; set; } - public object? SessionInfo { get; set; } - } -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs deleted file mode 100644 index 322337b..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/AuthorizationMessageHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Net.Http.Headers; - -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService; - -public class AuthorizationMessageHandler : DelegatingHandler -{ - private readonly IAuthTokenProvider _tokenProvider; - - public AuthorizationMessageHandler(IAuthTokenProvider tokenProvider) - { - _tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider)); - } - - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - try - { - var token = await _tokenProvider.GetTokenAsync(); - if (!string.IsNullOrWhiteSpace(token)) request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); - } - catch - { - // ignore token provider issues - } - - return await base.SendAsync(request, cancellationToken); - } -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs deleted file mode 100644 index 1519614..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/CookieAuthTokenProvider.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Microsoft.JSInterop; - -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService -{ - public class CookieAuthTokenProvider : IAuthTokenProvider - { - private readonly IJSRuntime _js; - private const string TokenKey = "token"; - private const int DefaultMaxAgeSeconds = 60 * 60; // 1 hour - - public CookieAuthTokenProvider(IJSRuntime js) - { - _js = js ?? throw new ArgumentNullException(nameof(js)); - } - - public async ValueTask GetTokenAsync() - { - try - { - // Read document.cookie via JS; returns string like "a=1; b=2" - var cookie = await _js.InvokeAsync("eval", "document.cookie"); - if (string.IsNullOrWhiteSpace(cookie)) return null; - - var parts = cookie.Split(';'); - foreach (var part in parts) - { - var trimmed = part.Trim(); - if (trimmed.StartsWith(TokenKey + "=", StringComparison.OrdinalIgnoreCase)) - { - return Uri.UnescapeDataString(trimmed.Substring(TokenKey.Length + 1)); - } - } - - return null; - } - catch - { - return null; - } - } - - public async ValueTask SetTokenAsync(string? token) - { - try - { - if (string.IsNullOrWhiteSpace(token)) - { - // Delete cookie - await _js.InvokeVoidAsync("eval", $"document.cookie = '{TokenKey}=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 GMT' "); - } - else - { - var safe = Uri.EscapeDataString(token); - // Use secure, samesite=strict and a default max-age - await _js.InvokeVoidAsync("eval", $"document.cookie = '{TokenKey}={safe}; path=/; max-age={DefaultMaxAgeSeconds}; samesite=strict' "); - } - } - catch - { - // ignore JS errors - } - } - } -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs deleted file mode 100644 index c74c9c5..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using OTManager.Web.ClientServices.DTOs.Identity; - -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService; - -public interface IAuthService -{ - Task RegisterAsync(RegisterRequest request); - Task LoginAsync(LoginRequest request); - Task LogOutAsync(); - Task GetTokenAsync(); -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs deleted file mode 100644 index 561d31e..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthStateNotifier.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService -{ - public interface IAuthStateNotifier - { - void NotifyUserAuthentication(string token); - void NotifyUserLogout(); - } -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs deleted file mode 100644 index eb14255..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/IAuthTokenProvider.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService -{ - public interface IAuthTokenProvider - { - ValueTask GetTokenAsync(); - ValueTask SetTokenAsync(string? token); - } -} diff --git a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs b/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs deleted file mode 100644 index 0fcbd8d..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/AuthenticationService/JSAuthTokenProvider.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.JSInterop; - -namespace OTManager.Web.ClientServices.Authorize.AuthenticationService -{ - public class JSAuthTokenProvider : IAuthTokenProvider - { - private readonly IJSRuntime _js; - private const string TokenKey = "token"; - - public JSAuthTokenProvider(IJSRuntime js) - { - _js = js ?? throw new ArgumentNullException(nameof(js)); - } - - public async ValueTask GetTokenAsync() - { - try - { - return await _js.InvokeAsync("localStorage.getItem", TokenKey); - } - catch - { - return null; - } - } - - public async ValueTask SetTokenAsync(string? token) - { - try - { - if (string.IsNullOrWhiteSpace(token)) - { - await _js.InvokeVoidAsync("localStorage.removeItem", TokenKey); - } - else - { - await _js.InvokeVoidAsync("localStorage.setItem", TokenKey, token); - } - } - catch - { - // ignore errors from JS - } - } - } -} diff --git a/src/OTManager.Web/ClientServices/Authorize/UserService.cs b/src/OTManager.Web/ClientServices/Authorize/UserService.cs deleted file mode 100644 index 34774d8..0000000 --- a/src/OTManager.Web/ClientServices/Authorize/UserService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using OTManager.Web.ClientServices.DTOs.Identity; - -namespace OTManager.Web.ClientServices.Authorize; - -public class UserService(HttpClient httpClient) -{ - private readonly HttpClient _httpClient = httpClient; - - public async Task> GetAll() - { - var users = await _httpClient.GetFromJsonAsync>("user"); - return users!.AsQueryable(); - } -} diff --git a/src/OTManager.Web/ClientServices/Clients/ClientService.cs b/src/OTManager.Web/ClientServices/Clients/ClientService.cs new file mode 100644 index 0000000..e947425 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Clients/ClientService.cs @@ -0,0 +1,6 @@ +namespace OTManager.Web.ClientServices.Clients; + +public class ClientService(HttpClient httpClient) +{ + private readonly HttpClient _httpClient = httpClient; +} diff --git a/src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs b/src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs deleted file mode 100644 index 135032d..0000000 --- a/src/OTManager.Web/ClientServices/DTOs/Enterprises/EnterpriseReadDto.cs +++ /dev/null @@ -1,20 +0,0 @@ -using OTManager.Web.ClientServices.DTOs.Identity; - -namespace OTManager.Web.ClientServices.DTOs.Enterprises; - -public record EnterpriseReadDto -{ - public string Id { get; set; } = default!; - public string EnterpriseName { get; set; } = default!; - public string Description { get; set; } = default!; - - public IEnumerable Users { get; set; } = []; - - public DateTime? CreatedAt { get; set; } - public DateTime? UpdatedAt { get; set; } - public DateTime? DeletedAt { get; set; } - - public bool IsActive { get; set; } - public bool IsSortable { get; set; } - public bool IsDeleted { get; set; } -} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs deleted file mode 100644 index 01e1ae7..0000000 --- a/src/OTManager.Web/ClientServices/DTOs/Identity/JwtSettings.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace OTManager.Web.ClientServices.DTOs.Identity -{ - public class JwtSettings - { - } -} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs deleted file mode 100644 index e96123e..0000000 --- a/src/OTManager.Web/ClientServices/DTOs/Identity/LoginRequest.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace OTManager.Web.ClientServices.DTOs.Identity -{ - public record LoginRequest(string Username, string Password); -} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs deleted file mode 100644 index 0b7ae74..0000000 --- a/src/OTManager.Web/ClientServices/DTOs/Identity/LoginResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace OTManager.Web.ClientServices.DTOs.Identity -{ - public record LoginResponse - ( - bool Success, - string? Token, - string? Message - ); -} diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs deleted file mode 100644 index 6d57f3e..0000000 --- a/src/OTManager.Web/ClientServices/DTOs/Identity/RegisterRequest.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace OTManager.Web.ClientServices.DTOs.Identity; - -public record RegisterRequest -(string Email, string Password); diff --git a/src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs b/src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs deleted file mode 100644 index 7c71ef5..0000000 --- a/src/OTManager.Web/ClientServices/DTOs/Identity/UserReadDto.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace OTManager.Web.ClientServices.DTOs.Identity; - -public record UserReadDto -{ - public string Id { get; set; } = string.Empty; - public string UserName { get; set; } = string.Empty; - public string Email { get; set; } = string.Empty; - public IList Role { get; set; } = new List(); -} diff --git a/src/OTManager.Web/ClientServices/Materials/MaterialService.cs b/src/OTManager.Web/ClientServices/Materials/MaterialService.cs new file mode 100644 index 0000000..c079395 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Materials/MaterialService.cs @@ -0,0 +1,29 @@ +using OTManager.Core.QueryParams; +using OTManager.Web.ClientServices.Materials.Records; + +namespace OTManager.Web.ClientServices.Materials; + +public class MaterialService(HttpClient httpClient) +{ + private readonly HttpClient _httpClient = httpClient; + + public async Task> GetFilteredAsync(MaterialQueryParams materialQuery) + { + var result = await _httpClient.PostAsJsonAsync($"material/filter", materialQuery); + //var response = await _httpClient.GetFromJsonAsync>($"material"); + + List? materials = []; + + if (result.IsSuccessStatusCode) + { + materials = await result.Content.ReadFromJsonAsync>(); + } + + return materials!; + } + public async Task GetByIdAsync(string id) + { + var response = await _httpClient.GetFromJsonAsync($"material/{id}"); + return response!; + } +} diff --git a/src/OTManager.Web/ClientServices/Materials/Records/MaterialReadDto.cs b/src/OTManager.Web/ClientServices/Materials/Records/MaterialReadDto.cs new file mode 100644 index 0000000..cd9e468 --- /dev/null +++ b/src/OTManager.Web/ClientServices/Materials/Records/MaterialReadDto.cs @@ -0,0 +1,8 @@ +namespace OTManager.Web.ClientServices.Materials.Records; + +public record MaterialReadDto +(Guid Id, +string Code, +string Name, +string MeasureUnit, +decimal UnitCost); diff --git a/src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor b/src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor deleted file mode 100644 index 9ed3fb0..0000000 --- a/src/OTManager.Web/Components/Account/Pages/AccountManagementPage.razor +++ /dev/null @@ -1,5 +0,0 @@ -

AccountManagementPage

- -@code { - -} diff --git a/src/OTManager.Web/Components/Account/Pages/Auth/Login.razor b/src/OTManager.Web/Components/Account/Pages/Auth/Login.razor index 186b5bb..b30c2c9 100644 --- a/src/OTManager.Web/Components/Account/Pages/Auth/Login.razor +++ b/src/OTManager.Web/Components/Account/Pages/Auth/Login.razor @@ -1,6 +1,6 @@ @page "/login" @using Microsoft.AspNetCore.Components.Authorization -@inject OTManager.Web.ClientServices.Authorize.AuthenticationService.IAuthService AuthService + @inject NavigationManager Nav @inject AuthenticationStateProvider AuthStateProvider @@ -8,7 +8,7 @@ - +@* *@

@message

@code { @@ -16,21 +16,21 @@ private string password = string.Empty; private string message = string.Empty; - private async Task HandleLogin() - { - var res = await AuthService.LoginAsync(new OTManager.Web.ClientServices.DTOs.Identity.LoginRequest(username, password)); - if (res is null || !res.Success) - { - message = res?.Message ?? "Error en login"; - return; - } + // private async Task HandleLogin() + // { + // var res = await AuthService.LoginAsync(new OTManager.Web.ClientServices.DTOs.Identity.LoginRequest(username, password)); + // if (res is null || !res.Success) + // { + // message = res?.Message ?? "Error en login"; + // return; + // } - // Notify provider in case not already done - if (AuthStateProvider is OTManager.Web.ClientServices.Authorize.AuthenticationService.ApiAuthenticationStateProvider provider) - { - provider.NotifyUserAuthentication(res.Token!); - } + // // Notify provider in case not already done + // if (AuthStateProvider is OTManager.Web.ClientServices.Authorize.AuthenticationService.ApiAuthenticationStateProvider provider) + // { + // provider.NotifyUserAuthentication(res.Token!); + // } - Nav.NavigateTo("/"); - } + // Nav.NavigateTo("/"); + // } } diff --git a/src/OTManager.Web/Components/Account/Pages/Auth/Register.razor b/src/OTManager.Web/Components/Account/Pages/Auth/Register.razor index f5b0834..c950608 100644 --- a/src/OTManager.Web/Components/Account/Pages/Auth/Register.razor +++ b/src/OTManager.Web/Components/Account/Pages/Auth/Register.razor @@ -1,12 +1,12 @@ @page "/register" -@inject OTManager.Web.ClientServices.Authorize.AuthenticationService.IAuthService AuthService + @inject NavigationManager Nav

Register

- +@* *@

@message

@code { @@ -14,16 +14,16 @@ private string password = string.Empty; private string message = string.Empty; - private async Task HandleRegister() - { - var ok = await AuthService.RegisterAsync(new OTManager.Web.ClientServices.DTOs.Identity.RegisterRequest(email, password)); - if (ok) - { - Nav.NavigateTo("/login"); - } - else - { - message = "Error registrando usuario"; - } - } + // private async Task HandleRegister() + // { + // var ok = await AuthService.RegisterAsync(new OTManager.Web.ClientServices.DTOs.Identity.RegisterRequest(email, password)); + // if (ok) + // { + // Nav.NavigateTo("/login"); + // } + // else + // { + // message = "Error registrando usuario"; + // } + // } } diff --git a/src/OTManager.Web/Components/Account/Pages/User/Profile.razor b/src/OTManager.Web/Components/Account/Pages/User/Profile.razor index f5dd061..b99f49b 100644 --- a/src/OTManager.Web/Components/Account/Pages/User/Profile.razor +++ b/src/OTManager.Web/Components/Account/Pages/User/Profile.razor @@ -20,18 +20,18 @@ private string email = string.Empty; private List roles = new(); - protected override async Task OnInitializedAsync() - { - var authState = await AuthStateProvider.GetAuthenticationStateAsync(); - var user = authState.User; - if (user.Identity?.IsAuthenticated == true) - { - userName = user.Identity.Name ?? user.Claims.FirstOrDefault(c => c.Type == "unique_name")?.Value ?? string.Empty; - email = user.Claims.FirstOrDefault(c => c.Type == "email")?.Value ?? string.Empty; - roles = user.Claims - .Where(c => c.Type == "role" || c.Type == "roles" || c.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role") - .Select(c => c.Value) - .ToList(); - } - } + // protected override async Task OnInitializedAsync() + // { + // var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + // var user = authState.User; + // if (user.Identity?.IsAuthenticated == true) + // { + // userName = user.Identity.Name ?? user.Claims.FirstOrDefault(c => c.Type == "unique_name")?.Value ?? string.Empty; + // email = user.Claims.FirstOrDefault(c => c.Type == "email")?.Value ?? string.Empty; + // roles = user.Claims + // .Where(c => c.Type == "role" || c.Type == "roles" || c.Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/role") + // .Select(c => c.Value) + // .ToList(); + // } + // } } diff --git a/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor b/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor index 7f15666..b1187c3 100644 --- a/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor +++ b/src/OTManager.Web/Components/Clients/Components/ClientListViewComponent.razor @@ -1,4 +1,4 @@ - + - + diff --git a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor index 4f5fc4f..a10f591 100644 --- a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor +++ b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor @@ -11,59 +11,6 @@ - - @if (OverseeItems is null) - { - Noting to see here - } - else - { - foreach (Item item in OverseeItems) - { - - - @item.Code - @item.Name - @item.UnitCost - - - } - } - - -@* - @if (OverseeItems is null) - { - Noting to see here - } - else - { - for (int i = 0; i < OverseeItems.Count; i++) - { - - - @OverseeItems[i].Code - @OverseeItems[i].Name - @OverseeItems[i].UnitCost - X - - - } - } - *@ - @@ -72,10 +19,7 @@ @if (Items is null) { - +

Add some

} else @@ -85,10 +29,12 @@ + LastModification="DateTime.UtcNow" + Status="Empty" + Username="Anonymous" + OnDeleteEvent="@OnDeleteEvent" + OnEditEvent="@OnEditEvent" + OnOverseeEvent="@OnOverseeEvent" /> } diff --git a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs index a790eff..d398aaf 100644 --- a/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs +++ b/src/OTManager.Web/Components/Clients/Pages/ClientsIndex.razor.cs @@ -1,14 +1,8 @@ -using System.Collections.ObjectModel; - -using Microsoft.FluentUI.AspNetCore.Components; - -namespace OTManager.Web.Components.Clients.Pages; +namespace OTManager.Web.Components.Clients.Pages; public partial class ClientsIndex { private IQueryable? Items { get; set; } - private ObservableCollection OverseeItems { get; set; } = []; - private int SelectedIndex = 0; private Item? NewItem { get; set; } @@ -16,45 +10,19 @@ protected override async Task OnInitializedAsync() { await Task.Delay(500); Items = GetDemoItems(); - OverseeItems.CollectionChanged += (_, __) - => InvokeAsync(StateHasChanged); } - public void OverseeClient(Item item) + private void OnDeleteEvent() { - OverseeItems.Add(item); - } - public void StopOverseeByIndex(int index) + } + private void OnEditEvent() { - if (index < 0 || index >= OverseeItems.Count) return; - var wasSelected = SelectedIndex == index; - OverseeItems.RemoveAt(index); - if (OverseeItems.Count == 0) { - SelectedIndex = -1; - InvokeAsync(StateHasChanged); - return; - } - - if (wasSelected) - { - SelectedIndex = Math.Min(index, OverseeItems.Count - 1); - } - else if (index < SelectedIndex) - { - SelectedIndex--; - } - - InvokeAsync(StateHasChanged); } - - public void HandleTabClose(FluentTab tab) + private void OnOverseeEvent() { - var client = (Item)tab.Data!; - var index = OverseeItems.IndexOf(client); - if (index == -1) return; - StopOverseeByIndex(index); + } public static IQueryable GetDemoItems() => new List{ diff --git a/src/OTManager.Web/Components/Clients/_Imports.razor b/src/OTManager.Web/Components/Clients/_Imports.razor index 2d3fc19..146f66b 100644 --- a/src/OTManager.Web/Components/Clients/_Imports.razor +++ b/src/OTManager.Web/Components/Clients/_Imports.razor @@ -1 +1,2 @@ -@using OTManager.Web.Components.Clients.Components \ No newline at end of file +@using OTManager.Web.Components.Clients.Components +@using OTManager.WebComp.Shared \ No newline at end of file diff --git a/src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor b/src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor index 28ed318..ef8ca8e 100644 --- a/src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor +++ b/src/OTManager.Web/Components/Materials/Components/MaterialListViewComponent.razor @@ -1,4 +1,4 @@ - + + + + Materials + + + + + + + + + + + + @Body + + diff --git a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor index 3229842..d31d22a 100644 --- a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor +++ b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor @@ -1,21 +1,10 @@ @page "/Materials/Index" -Materials | Index - - +@layout MaterialLayout - - Materials - - - - - +@inject MaterialService _materialService; - - - +Materials | Index @if (Items is null) @@ -29,7 +18,7 @@ } else { - foreach (Item item in Items) + foreach (MaterialReadDto item in Items) { diff --git a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs index 0275bc6..8d0d591 100644 --- a/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs +++ b/src/OTManager.Web/Components/Materials/Pages/StocksIndex.razor.cs @@ -1,29 +1,47 @@ -namespace OTManager.Web.Components.Materials.Pages; +using OTManager.Core.QueryParams; +using OTManager.Web.ClientServices.Materials.Records; + +namespace OTManager.Web.Components.Materials.Pages; public partial class StocksIndex { - private IQueryable? Items { get; set; } + private MaterialQueryParams query = new( + Search: "PitBoy", + Code: string.Empty, + Name: string.Empty, + CreatedAt: null, + SortBy: "Name", + Descending: false, + Page: 1, + PageSize: 3); + + private IQueryable? Items { get; set; } + private MaterialQueryParams Query { get => query; set => query = value; } + private MaterialReadDto? Item { get; set; } protected override async Task OnInitializedAsync() { await Task.Delay(500); - Items = GetDemoItems(); + await GetItemsAsync(); } - public static IQueryable GetDemoItems() => new List{ - new ("001", "Material 1", "U", 13.5m ), - new ("002", "Material 2", "U", 13.5m), - new ("003", "Material 3", "U", 13.5m), - new ("004", "Material 4", "U", 13.5m), - new ("005", "Material 5", "U", 13.5m), - new ("006", "Material 6", "U", 13.5m) - }.AsQueryable(); + public async Task GetItemsAsync() + { + var response = await _materialService.GetFilteredAsync(Query); + Items = response.AsQueryable(); + } - public record Item( - string Code, - string Name, - string MeasureUnit, - decimal UnitCost - ); + private async Task GetItemByIdAsync(string id) + { + var response = await _materialService.GetByIdAsync(id); + if (response is not null) + { + Item = response; + } + else + { + Item = null; + } + } } diff --git a/src/OTManager.Web/Components/Materials/_Imports.razor b/src/OTManager.Web/Components/Materials/_Imports.razor index b45aa95..6e288f4 100644 --- a/src/OTManager.Web/Components/Materials/_Imports.razor +++ b/src/OTManager.Web/Components/Materials/_Imports.razor @@ -1 +1,5 @@ -@using OTManager.Web.Components.Materials.Components \ No newline at end of file +@using OTManager.Web.Components.Materials.Components +@using OTManager.Web.ClientServices.Materials +@using OTManager.Web.ClientServices.Materials.Records +@using OTManager.Web.Components.Materials.Layout +@using OTManager.Web.Components.Layout \ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor b/src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor index 5553b03..143064f 100644 --- a/src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor +++ b/src/OTManager.Web/Components/Settings/Layout/SettingsLayout.razor @@ -1,6 +1,4 @@ -@using OTManager.Web.Components.Layout - -@layout MainLayout +@layout MainLayout @inherits LayoutComponentBase diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor index 3fdfe15..4bc125c 100644 --- a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor +++ b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor @@ -1,8 +1,6 @@ @page "/settings/users" -@using OTManager.Web.ClientServices.Authorize @layout SettingsLayout -@inject UserService uService - @if (users == null) + @* @if (users == null) {

Loading...

} else { - @* + - *@ - } + + } *@
\ No newline at end of file diff --git a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs index 922b251..ed5e7dd 100644 --- a/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs +++ b/src/OTManager.Web/Components/Settings/Pages/SettingUserPage.razor.cs @@ -1,31 +1,30 @@ -using OTManager.Web.ClientServices.DTOs.Identity; - + namespace OTManager.Web.Components.Settings.Pages; public partial class SettingUserPage { - public IQueryable? users; + //public IQueryable? users; - protected override async Task OnInitializedAsync() - { - // Simulate asynchronous loading to demonstrate a loading indicator - await Task.Delay(500); - await GetAllAsync(); + //protected override async Task OnInitializedAsync() + //{ + // // Simulate asynchronous loading to demonstrate a loading indicator + // await Task.Delay(500); + // await GetAllAsync(); - } + //} - private async Task GetAllAsync() - { - await uService.GetAll(); + //private async Task GetAllAsync() + //{ + // //await uService.GetAll(); - var response = await uService.GetAll(); - if (response is not null) - { - users = await uService.GetAll(); - } - else - { - users = null; - } - } + // //var response = await uService.GetAll(); + // //if (response is not null) + // //{ + // // users = await uService.GetAll(); + // //} + // //else + // //{ + // // users = null; + // //} + //} } diff --git a/src/OTManager.Web/Components/Settings/_Imports.razor b/src/OTManager.Web/Components/Settings/_Imports.razor index e69de29..208c16a 100644 --- a/src/OTManager.Web/Components/Settings/_Imports.razor +++ b/src/OTManager.Web/Components/Settings/_Imports.razor @@ -0,0 +1 @@ +@using OTManager.Web.Components.Layout \ No newline at end of file diff --git a/src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs b/src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs index 4cf2e16..a0a3e86 100644 --- a/src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs +++ b/src/OTManager.Web/Extensions/ApplicationDependencyInjection.cs @@ -1,6 +1,4 @@ -using System.Net; - -using Microsoft.FluentUI.AspNetCore.Components; +using Microsoft.FluentUI.AspNetCore.Components; namespace OTManager.Web.Extensions; diff --git a/src/OTManager.Web/OTManager.Web.csproj b/src/OTManager.Web/OTManager.Web.csproj index 666396f..cc618a2 100644 --- a/src/OTManager.Web/OTManager.Web.csproj +++ b/src/OTManager.Web/OTManager.Web.csproj @@ -7,8 +7,13 @@ - - - + + + + + + + + diff --git a/src/OTManager.Web/Program.cs b/src/OTManager.Web/Program.cs index a3f2f9b..b1bc5df 100644 --- a/src/OTManager.Web/Program.cs +++ b/src/OTManager.Web/Program.cs @@ -1,46 +1,37 @@ -using Microsoft.AspNetCore.Components.Authorization; - -using OTManager.Web.ClientServices.Authorize; -using OTManager.Web.ClientServices.Authorize.AuthenticationService; +using OTManager.Web.ClientServices.Materials; using OTManager.Web.Components; using OTManager.Web.Extensions; var builder = WebApplication.CreateBuilder(args); +builder.AddServiceDefaults(); + // Add services to the container. ApplicationDependencyInjection.AddApplicationDependency(builder.Services, builder.Configuration); -builder.Services.AddScoped(); -// Use cookie-based token storage in the browser via JS -builder.Services.AddScoped(); -// Authorization message handler depends on IAuthTokenProvider -builder.Services.AddTransient(); -// Register the authentication state provider and the notifier interface -builder.Services.AddScoped(); -builder.Services.AddScoped(sp => sp.GetRequiredService()); -builder.Services.AddScoped(sp => sp.GetRequiredService()); - -// Configure named HttpClient with AuthorizationMessageHandler +// ToDo: move the logic to extension class builder.Services.AddHttpClient("ApiClient", client => { client.BaseAddress = new Uri("https://localhost:7257/api/"); -}) -.AddHttpMessageHandler(); +}); -// Register UserService to use the named client "ApiClient" -builder.Services.AddScoped(sp => +builder.Services.AddScoped(sp => { var factory = sp.GetRequiredService(); var client = factory.CreateClient("ApiClient"); - return new UserService(client); + return new MaterialService(client); }); +// ^^ move the logic to extension class ^^ + builder.Services.AddOptions(); builder.Services.AddAuthorizationCore(); // Use AddAuthorizationCore for Blazor var app = builder.Build(); +app.MapDefaultEndpoints(); + app.AddLocalizationBuilder(); // Configure the HTTP request pipeline. diff --git a/src/OTManager.Web/wwwroot/app.css b/src/OTManager.Web/wwwroot/app.css index 77cf978..f4e0c44 100644 --- a/src/OTManager.Web/wwwroot/app.css +++ b/src/OTManager.Web/wwwroot/app.css @@ -189,3 +189,16 @@ code { right: unset; } } + +.glass-panel { + background: rgba(255,255,255,0.2); + backdrop-filter: blur(10px) saturate(150%); + -webkit-backdrop-filter: blur(10px) saturate(150%); + box-shadow: 0 4px 20px rgba(0,0,0,0.2); + transition: all 0.3s ease-in-out; +} + + .glass-panel:hover { + background: rgba(255,255,255,0.3); + transform: translateY(-2px); + } diff --git a/src/OTManager.Web/wwwroot/css/RHStiles.css b/src/OTManager.Web/wwwroot/css/RHStiles.css new file mode 100644 index 0000000..e02abfc --- /dev/null +++ b/src/OTManager.Web/wwwroot/css/RHStiles.css @@ -0,0 +1 @@ + diff --git a/src/OTManager.WebComp/OTManager.WebComp.csproj b/src/OTManager.WebComp/OTManager.WebComp.csproj new file mode 100644 index 0000000..a6675cc --- /dev/null +++ b/src/OTManager.WebComp/OTManager.WebComp.csproj @@ -0,0 +1,22 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/src/OTManager.Web/Components/Clients/Components/ContextMenuComponent.razor b/src/OTManager.WebComp/Shared/ContextMenuComponent.razor similarity index 100% rename from src/OTManager.Web/Components/Clients/Components/ContextMenuComponent.razor rename to src/OTManager.WebComp/Shared/ContextMenuComponent.razor diff --git a/src/OTManager.WebComp/_Imports.razor b/src/OTManager.WebComp/_Imports.razor new file mode 100644 index 0000000..02d7739 --- /dev/null +++ b/src/OTManager.WebComp/_Imports.razor @@ -0,0 +1,4 @@ +@using Microsoft.AspNetCore.Components.Web + +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.FluentUI.AspNetCore.Components \ No newline at end of file diff --git a/src/OTManager.WebComp/wwwroot/background.png b/src/OTManager.WebComp/wwwroot/background.png new file mode 100644 index 0000000000000000000000000000000000000000..e15a3bde6e2bdb380df6a0b46d7ed00bdeb0aaa8 GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK1SGdsl%54rjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucLCF%=h?3y^w370~qEv>0#LT=By}Z;C1rt33 zJwr2>%=KS^ie7oTIEF;HpS|GCbyPusHSqiXaCu3qf)82(9Gq&mZq2{Kq}M*X&MWtJ zSi1Jo7ZzfImg%g=t(qo=wsSR2lZoP(Rj#3wacN=q0?Br(rXzgZEGK2$ID{|A=5S{xJEuzSH>!M+7wSY6hB<=-E^*n0W7 S8wY^CX7F_Nb6Mw<&;$S{dxtsz literal 0 HcmV?d00001 diff --git a/src/OTManager.WebComp/wwwroot/exampleJsInterop.js b/src/OTManager.WebComp/wwwroot/exampleJsInterop.js new file mode 100644 index 0000000..ea8d76a --- /dev/null +++ b/src/OTManager.WebComp/wwwroot/exampleJsInterop.js @@ -0,0 +1,6 @@ +// This is a JavaScript module that is loaded on demand. It can export any number of +// functions, and may import other JavaScript modules if required. + +export function showPrompt(message) { + return prompt(message, 'Type anything here'); +} From 697ac920a9243ae35052f49f6674f02ba8447441 Mon Sep 17 00:00:00 2001 From: Adrian Mesa Sacasas Date: Sun, 2 Nov 2025 13:47:28 -0500 Subject: [PATCH 4/4] =?UTF-8?q?Actualiza=20documentaci=C3=B3n=20y=20a?= =?UTF-8?q?=C3=B1ade=20archivos=20de=20contribuci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Se ha actualizado el archivo `LICENSE.txt` para incluir el nombre completo del autor y se han realizado correcciones en el texto de la licencia. En `README.md`, se ha cambiado el título del proyecto y se ha añadido información sobre la estructura del proyecto, así como instrucciones detalladas sobre cómo empezar a trabajar con el mismo. También se han añadido secciones sobre contribuciones, seguridad y conducta, y se han eliminado líneas redundantes. Se ha añadido un archivo `.editorconfig` para establecer las configuraciones de estilo de código y formato para el proyecto. Se ha creado un `Code_Of_Conduct.md` que establece normas de comportamiento y cómo reportar incumplimientos, promoviendo un entorno inclusivo y respetuoso. Se ha añadido un `CONTRIBUTING.md` que proporciona pautas para contribuir al proyecto, incluyendo cómo reportar errores y proponer nuevas funcionalidades. Se ha creado un `SECURITY.md` que describe el proceso para reportar vulnerabilidades y las prácticas mínimas de seguridad que deben seguirse en el proyecto. Se ha añadido un archivo `CODEOWNERS` para definir los responsables de las diferentes áreas del proyecto. Se ha creado una plantilla para Pull Requests (`PULL_REQUEST_TEMPLATE.md`) que incluye secciones para describir el cambio, cómo probarlo y un checklist. Se han añadido plantillas para reportar errores (`bug_report.md`) y solicitar nuevas funcionalidades (`feature_request.md`), facilitando la comunicación y el seguimiento de problemas y mejoras en el proyecto. --- .editorconfig | 19 ++++ .github/CODEOWNERS | 12 +++ .github/ISSUE_TEMPLATE/bug_report.md | 29 +++++ .github/ISSUE_TEMPLATE/feature_request.md | 23 ++++ .github/PULL_REQUEST_TEMPLATE.md | 23 ++++ CONTRIBUTING.md | 120 +++++++++++++++++++++ Code_Of_Conduct.md | 38 +++++++ LICENSE.txt | 2 +- README.md | 64 ++++++----- SECURITY.md | 48 +++++++++ src/OTManager.Web/wwwroot/Docs/LICENSE.txt | 21 ---- src/OTManager.Web/wwwroot/Docs/README.md | 40 ------- 12 files changed, 351 insertions(+), 88 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CONTRIBUTING.md create mode 100644 Code_Of_Conduct.md create mode 100644 SECURITY.md delete mode 100644 src/OTManager.Web/wwwroot/Docs/LICENSE.txt delete mode 100644 src/OTManager.Web/wwwroot/Docs/README.md diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4ef74ab --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +charset = utf-8 +end_of_line = crlf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{cs,cshtml}] +indent_style = space +indent_size = 4 +dotnet_diagnostic.IDE0055.severity = suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = false:none + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..901f471 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,12 @@ +# CODEOWNERS file +# Propietarios por área. Reemplaza o añade GitHub handles reales según corresponda. + +# Responsable principal mantiene todo +* @adrianmesasacasas + +# Áreas específicas (asignadas temporalmente al responsable principal) +/src/OTManager.Api/ @adrianmesasacasas +/src/OTManager.Web/ @adrianmesasacasas +/src/OTManager.Data/ @adrianmesasacasas + +# Nota: para añadir más revisores automáticos, reemplaza los handles anteriores por los nombres reales de los colaboradores o equipos (p. ej. @org/eq-api). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..4f36d2f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug report +about: Reporta un error encontrado en el proyecto +title: "bug: " +labels: bug +assignees: '' +--- + +**Descripcin breve** +Una descripcin clara y concisa del problema. + +**Pasos para reproducir** +1. +2. +3. + +**Comportamiento esperado** + +**Comportamiento observado** + +**Datos adicionales** +- Versin de .NET: .NET 9 +- Sistema operativo: +- Logs: + +**Prioridad** +- [ ] baja +- [ ] media +- [ ] alta diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..6fb6b4e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Proponer una nueva funcionalidad +title: "feat: " +labels: enhancement +assignees: '' +--- + +**Propuesta** +Describe la funcionalidad solicitada y el problema que resuelve. + +**Casos de uso** +- + +**Propuesta de diseo** +- Endpoints/API propuestos: +- Cambios en UI: + +**Impacto** +- Es breaking change? (s/no) +- Compatibilidad hacia atrs: + +**Notas adicionales** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..2b9597e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ +# Tipo de cambio +- [ ] feat +- [ ] fix +- [ ] docs +- [ ] style +- [ ] refactor +- [ ] test +- [ ] chore + +## Descripcin +Explica brevemente qu hace este PR y por qu es necesario. + +## Cmo probar +Instrucciones para reproducir y validar los cambios. + +## Checklist +- [ ] El cdigo compila y pasa las pruebas locales +- [ ] Se agregaron/actualizaron pruebas cuando aplica +- [ ] Se actualiz la documentacin si aplica +- [ ] No se aadieron secretos ni datos sensibles + +## Issue relacionada +Closes # (issue) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..051bab3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,120 @@ +CONTRIBUTING +=========== + +Gracias por tu inters en contribuir a OTManager.Wapi. Este documento proporciona las pautas esenciales para colaborar en este proyecto de software libre. Para evitar duplicar informacin, las secciones sobre seguridad y conducta se encuentran en SECURITY.md y Code_Of_Conduct.md respectivamente. + +1. Resumen +--------- +OTManager.Wapi es un proyecto en .NET 9 (API y Blazor) que utiliza Entity Framework Core para persistencia. Intentamos mantener contribuciones claras, probadas y acordes con las reglas de estilo del proyecto. + +2. Cdigo de conducta +--------------------- +Revisa `Code_Of_Conduct.md` para las normas de comportamiento y cmo reportar incumplimientos. + +3. Cmo reportar un bug +----------------------- +- Abre una Issue nueva usando la plantilla "Bug report" (usa 'New issue' -> seleccionar plantilla). +- Incluye pasos para reproducir, comportamiento esperado, comportamiento real, versin de .NET (debe ser .NET 9), sistema operativo, y logs relevantes. + +4. Cmo proponer una caracterstica +---------------------------------- +- Abre una Issue usando la plantilla "Feature request" describiendo el caso de uso y la solucin propuesta. + +5. Flujo de trabajo para contribuir (fork & PR) +----------------------------------------------- +1. Haz fork del repositorio y clona tu fork: + ```bash + git clone https://github.com/tu-usuario/OTManager.Wapi.git + cd OTManager.Wapi + ``` +2. Crea una rama para tu trabajo (nombres sugeridos): + - feature/ + - fix/ + - docs/ + + ```bash + git checkout -b feature/mi-nueva-funcionalidad + ``` +3. Asegrate de que tu rama est actualizada con la rama principal upstream: + ```bash + git remote add upstream https://github.com/organizacion/OTManager.Wapi.git + git fetch upstream + git rebase upstream/main + ``` +4. Realiza cambios pequeos y con commits atmicos. +5. Ejecuta pruebas y formatea el cdigo (ver seccin herramientas). +6. Publica tu rama y abre un Pull Request usando la plantilla PR disponible. + +6. Requisitos y herramientas para el desarrollo +---------------------------------------------- +- .NET 9 SDK instalado +- SQL Server (o proveedor EF Core compatible) para ejecutar migraciones locales +- dotnet-ef (si trabajas con migraciones): + ```bash + dotnet tool install --global dotnet-ef + ``` + +7. Ejecutar el proyecto localmente +--------------------------------- +- Restaurar dependencias: + ```bash + dotnet restore + ``` +- Aplicar migraciones (si corresponde): + ```bash + dotnet ef database update --project src/OTManager.Data/OTManager.Data.csproj --startup-project src/OTManager.Api/OTManager.Api.csproj + ``` +- Ejecutar la API y la UI: + ```bash + dotnet run --project src/OTManager.Api/OTManager.Api.csproj + dotnet run --project src/OTManager.Web/OTManager.Web.csproj + ``` + +8. Estilo de cdigo y linters +---------------------------- +- Sigue las reglas de C# y .editorconfig del repositorio (si existe). +- Usa dotnet format para formatear tu cdigo antes de subirlo: + ```bash + dotnet tool restore + dotnet format + ``` + +9. Pruebas +---------- +- Aade/actualiza pruebas unitarias para cualquier cambio en la lgica de negocio. +- Ejecuta todas las pruebas antes de abrir PR: + ```bash + dotnet test + ``` + +10. Convenciones de commits +--------------------------- +Usa mensajes claros y preferiblemente el estndar "Conventional Commits": +- feat: Nueva funcionalidad +- fix: Correccin de bug +- docs: Cambios en documentacin +- style: Formateo, espacios, punto y coma +- refactor: Refactorizacin sin cambiar comportamiento +- test: Aadir/ajustar pruebas +- chore: Cambios en build o herramientas + +11. Proceso de Pull Request +--------------------------- +- Crea PR contra la rama main (o la rama de desarrollo si el proyecto la usa). +- Usa la plantilla de PR ubicada en `.github/PULL_REQUEST_TEMPLATE.md`. +- Describe qu hace el cambio, por qu es necesario y cmo probarlo. + +Checklist sugerida para PRs (ver plantilla): +- [ ] El cdigo compila y pasa las pruebas locales +- [ ] Agregado/actualizado tests +- [ ] Documentacin actualizada si aplica +- [ ] Se formate el cdigo +- [ ] No incluye secretos en el commit + +12. Seguridad +------------- +Revisa `SECURITY.md` para el proceso de reporte de vulnerabilidades y prcticas de seguridad. + +13. Contacto +----------- +Si tienes dudas sobre cmo contribuir, abre una Issue etiquetada como "help wanted" o contacta a los mantenedores. diff --git a/Code_Of_Conduct.md b/Code_Of_Conduct.md new file mode 100644 index 0000000..20205dd --- /dev/null +++ b/Code_Of_Conduct.md @@ -0,0 +1,38 @@ +Cdigo de Conducta (Code of Conduct) +===================================== + +Nuestro objetivo es crear un proyecto inclusivo y respetuoso donde todas las personas se sientan bienvenidas y con disposicin para colaborar. + +1. Normas de comportamiento +--------------------------- +- Trata a todas las personas con respeto y profesionalismo. +- S constructivo en la crtica: enfcate en el cdigo y el contenido, no en la persona. +- Evita lenguaje ofensivo, despectivo, discriminatorio o sexualmente explcito. +- Respeta la diversidad de orgenes, opiniones, identidades y niveles de experiencia. + +2. Alcance +--------- +Este cdigo aplica a todos los mbitos relacionados con el proyecto: Issues, Pull Requests, revisiones de cdigo, discusiones en repositorios, canales de chat vinculados, listas de correo y eventos asociados al proyecto. + +3. Cmo reportar un incumplimiento +--------------------------------- +Si eres testigo o vctima de una conducta que viola este cdigo: +- Abre una Issue privada (si tu plataforma lo permite) etiquetada como `conduct` o `confidential`. +- Alternativamente, contacta a los mantenedores por correo electrnico (aade la direccin de contacto en el README o en la seccin de mantenedores). +- Proporciona informacin suficiente: descripcin del incidente, participantes, fecha/hora, enlaces relevantes y, si es posible, evidencia. + +4. Proceso de resolucin +------------------------ +- Los mantenedores revisarn el reporte y podrn solicitar informacin adicional. +- En casos claros de violacin, se podr pedir disculpas pblicas/privadas, remover contenido, suspender permisos de colaboracin o expulsar del proyecto. +- Las decisiones se aplicarn con proporcionalidad y registro interno. + +5. Confidencialidad +------------------- +Los reportes y la informacin sensible se manejarn de forma confidencial en la medida de lo posible. + +6. Implementacin y actualizacin +--------------------------------- +Este documento puede actualizarse con el tiempo. Los mantenedores publicarn la fecha de la ltima revisin y la versin del documento. + +Gracias por contribuir a un entorno seguro y respetuoso. \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index c7b086f..8cfa5aa 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Adrian Mesa +Copyright (c) 2024 Adrian Mesa Sacasas Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 56ac08a..51e6162 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,22 @@ -# OTManager.Wapi +# OTManager OTManager.Wapi es una solucin para la gestin de rdenes de trabajo, clientes, materiales, servicios y costos en entornos empresariales. El proyecto est desarrollado en .NET 9 y sigue una arquitectura limpia y modular. +Para evitar duplicar informacin, la documentacin detallada sobre contribucin, seguridad y conducta se mantiene en archivos dedicados en la raz del repositorio: + +- CONTRIBUTING.md Pautas para contribuir, flujo de trabajo y checklist de PR. +- SECURITY.md Pautas para reportar vulnerabilidades y prcticas mnimas de seguridad. +- Code_Of_Conduct.md Cdigo de conducta del proyecto. + +Tambin se incluyen plantillas de Issue y Pull Request en .github/ para estandarizar contribuciones. + ## Caractersticas principales -- **Gestin de rdenes de Trabajo**: Creacin, actualizacin y seguimiento de rdenes de trabajo. -- **Gestin de Clientes**: Administracin de informacin de clientes y su historial de rdenes. -- **Control de Materiales y Servicios**: Registro y control de materiales y servicios asociados a cada orden. -- **Costos y Facturacin**: Clculo y gestin de costos de materiales, servicios y mano de obra, as como generacin de facturas. -- **Auditora**: Seguimiento de cambios y acciones realizadas sobre las entidades principales. -- **API RESTful**: Exposicin de endpoints seguros y documentados para integracin con otros sistemas. -- **Documentacin interactiva (Swagger)**: Acceso restringido a la documentacin interactiva en entornos de desarrollo. -- **Unit of Work y Repositorios**: Patrn Unit of Work y repositorios para una gestin eficiente y desacoplada de la persistencia. -- **Arquitectura modular**: Separacin clara entre capa de datos, lgica de negocio, API y aplicacin. - -## Patrones de diseo implementados - -- **Unit of Work**: Para la gestin de transacciones y persistencia de datos de forma atmica. -- **Repository**: Para el acceso desacoplado a la capa de datos. -- **DTO (Data Transfer Object)**: Para la transferencia de datos entre capas y la API. -- **Dependency Injection**: Para la inyeccin de dependencias y facilitar el testing y la extensibilidad. -- **Controller/Endpoint**: Para la exposicin de la lgica de negocio a travs de una API RESTful. -- **Mapper**: Para la conversin entre entidades de dominio y DTOs. -- **Service Layer**: Para la encapsulacin de la lgica de negocio y orquestacin de operaciones. -- **Auditable Entity**: Para el seguimiento de cambios y auditora en las entidades principales. +- Gestin de rdenes de Trabajo, Clientes, Materiales y Servicios. +- Costos y Facturacin. +- Auditora y trazabilidad de cambios. +- API RESTful con documentacin (Swagger) en entornos de desarrollo. +- Arquitectura modular con Unit of Work, Repositories y Service Layer. ## Estructura del proyecto @@ -31,10 +24,29 @@ OTManager.Wapi es una soluci - `OTManager.Core`: Entidades de dominio y contratos. - `OTManager.App`: Lgica de negocio y servicios de aplicacin. - `OTManager.Api`: API RESTful y documentacin Swagger. - -## Requisitos -- .NET 9 SDK -- SQL Server u otro proveedor compatible con Entity Framework Core +- `OTManager.Web`: Interfaz de usuario basada en Blazor. +- `OTManager.WebComp`: Componentes compartidos para la interfaz de usuario. + +## Cmo empezar (resumen) + +1. Clona el repositorio: + ```bash + git clone https://github.com/tu-usuario/OTManager.Wapi.git + cd OTManager.Wapi + ``` +2. Restaura dependencias y ejecuta: + ```bash + dotnet restore + dotnet run --project src/OTManager.Api/OTManager.Api.csproj + dotnet run --project src/OTManager.Web/OTManager.Web.csproj + ``` +3. Configura la base de datos en `appsettings.json` y aplica migraciones si corresponde: + ```bash + dotnet ef database update --project src/OTManager.Data/OTManager.Data.csproj --startup-project src/OTManager.Api/OTManager.Api.csproj + ``` + +Para informacin detallada sobre contribuciones, seguridad, plantillas y cdigo de conducta revisa los archivos mencionados al inicio. ## Licencia -Este proyecto est bajo la Licencia MIT. Consulta el archivo LICENSE.txt para ms detalles. \ No newline at end of file + +Este proyecto est bajo la Licencia MIT. Consulta el archivo [LICENSE.txt](./LICENSE.txt) para ms detalles. \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..4a86b29 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,48 @@ +Security +======== + +Este documento describe el proceso y pautas esenciales para reportar vulnerabilidades y las prcticas mnimas de seguridad que el proyecto debe seguir. + +1. Reporte de vulnerabilidades +------------------------------ +- No publiques vulnerabilidades en Issues pblicas. +- Para reportes confidenciales, utiliza la seccin "Security Advisories" de GitHub o contacta a los mantenedores por correo electrnico: adrianmesasacasas@outlok.com. +- Incluye en el reporte: resumen, pasos para reproducir, impacto potencial, versin afectada, y mitigaciones temporales si existen. + +2. Respuesta a incidentes +------------------------- +- Los mantenedores confirmarn recepcin en un plazo mximo de 72 horas. +- Se evaluar la severidad y se priorizar la correccin. +- Se coordinar la divulgacin responsable y se publicar un advisory pblico una vez exista parche disponible. + +3. Prcticas recomendadas de desarrollo +--------------------------------------- +- No incluir secretos en el repositorio (claves, contraseas, tokens). Usa archivos de configuracin locales y variables de entorno. +- Aadir un archivo .gitignore apropiado para evitar subir archivos sensibles. +- Usar HTTPS y TLS para comunicaciones en produccin. +- Validar y sanitizar todas las entradas de usuarios (servidor y cliente). +- Limitar exposicin de informacin sensible en errores y logs. +- Implementar autenticacin y autorizacin robusta (p. ej. JWT con expiracin, refresh tokens controlados, y verificacin de scopes/roles). +- Proteger endpoints administrativos y documentacin (Swagger) en entornos no-privados. +- Usar cifrado para datos sensibles en reposo y en trnsito cuando sea necesario. + +4. Gestin de dependencias +-------------------------- +- Mantener dependencias actualizadas y aplicar parches de seguridad. +- Ejecutar anlisis de vulnerabilidades en dependencias (p. ej. `dotnet list package --vulnerable` o herramientas como Dependabot). + +5. Pruebas y validaciones +------------------------- +- Incluir pruebas de seguridad automatizadas cuando sea posible (anlisis esttico, escaneo de dependencias, pruebas de penetracin en etapas de CI). +- Revisar entradas y lmites para evitar inyeccin SQL, XSS, CSRF, etc. + +6. Poltica de divulgacin +-------------------------- +- Se seguir una divulgacin responsable: notificar a los mantenedores, permitir un periodo razonable para parche, luego divulgar pblicamente los detalles. + +7. Contacto +----------- +- Reportes de seguridad: adrianmesasacasas@outlok.com +- Si no se proporciona un correo, usa GitHub "Security Advisories". + +Gracias por ayudar a mantener la seguridad del proyecto. \ No newline at end of file diff --git a/src/OTManager.Web/wwwroot/Docs/LICENSE.txt b/src/OTManager.Web/wwwroot/Docs/LICENSE.txt deleted file mode 100644 index c7b086f..0000000 --- a/src/OTManager.Web/wwwroot/Docs/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Adrian Mesa - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/OTManager.Web/wwwroot/Docs/README.md b/src/OTManager.Web/wwwroot/Docs/README.md deleted file mode 100644 index 56ac08a..0000000 --- a/src/OTManager.Web/wwwroot/Docs/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# OTManager.Wapi - -OTManager.Wapi es una solucin para la gestin de rdenes de trabajo, clientes, materiales, servicios y costos en entornos empresariales. El proyecto est desarrollado en .NET 9 y sigue una arquitectura limpia y modular. - -## Caractersticas principales - -- **Gestin de rdenes de Trabajo**: Creacin, actualizacin y seguimiento de rdenes de trabajo. -- **Gestin de Clientes**: Administracin de informacin de clientes y su historial de rdenes. -- **Control de Materiales y Servicios**: Registro y control de materiales y servicios asociados a cada orden. -- **Costos y Facturacin**: Clculo y gestin de costos de materiales, servicios y mano de obra, as como generacin de facturas. -- **Auditora**: Seguimiento de cambios y acciones realizadas sobre las entidades principales. -- **API RESTful**: Exposicin de endpoints seguros y documentados para integracin con otros sistemas. -- **Documentacin interactiva (Swagger)**: Acceso restringido a la documentacin interactiva en entornos de desarrollo. -- **Unit of Work y Repositorios**: Patrn Unit of Work y repositorios para una gestin eficiente y desacoplada de la persistencia. -- **Arquitectura modular**: Separacin clara entre capa de datos, lgica de negocio, API y aplicacin. - -## Patrones de diseo implementados - -- **Unit of Work**: Para la gestin de transacciones y persistencia de datos de forma atmica. -- **Repository**: Para el acceso desacoplado a la capa de datos. -- **DTO (Data Transfer Object)**: Para la transferencia de datos entre capas y la API. -- **Dependency Injection**: Para la inyeccin de dependencias y facilitar el testing y la extensibilidad. -- **Controller/Endpoint**: Para la exposicin de la lgica de negocio a travs de una API RESTful. -- **Mapper**: Para la conversin entre entidades de dominio y DTOs. -- **Service Layer**: Para la encapsulacin de la lgica de negocio y orquestacin de operaciones. -- **Auditable Entity**: Para el seguimiento de cambios y auditora en las entidades principales. - -## Estructura del proyecto - -- `OTManager.Data`: Acceso a datos, entidades y auditora. -- `OTManager.Core`: Entidades de dominio y contratos. -- `OTManager.App`: Lgica de negocio y servicios de aplicacin. -- `OTManager.Api`: API RESTful y documentacin Swagger. - -## Requisitos -- .NET 9 SDK -- SQL Server u otro proveedor compatible con Entity Framework Core - -## Licencia -Este proyecto est bajo la Licencia MIT. Consulta el archivo LICENSE.txt para ms detalles. \ No newline at end of file