From b0e4e6a0c82248c13d90f0a86dc0a46aaac6b3bf Mon Sep 17 00:00:00 2001 From: "swagath.bairi" Date: Thu, 15 May 2025 11:37:12 -0700 Subject: [PATCH 01/66] Added .net core api example --- .../DotnetCoreApi/DataAccess/AppDbContext.cs | 34 +++ .../DataAccess/Entities/Category.cs | 15 ++ .../DataAccess/Entities/Inventory.cs | 13 ++ .../DataAccess/Entities/Product.cs | 22 ++ .../DataAccess/Entities/ProductAttribute.cs | 13 ++ .../DataAccess/Entities/ProductPrice.cs | 15 ++ .../DataAccess/Entities/ProductReview.cs | 15 ++ .../CategoryConfiguration.cs | 41 ++++ .../InventoryConfiguration.cs | 40 ++++ .../ProductAttributeConfiguration.cs | 40 ++++ .../ProductConfiguration.cs | 81 ++++++++ .../ProductPriceConfiguration.cs | 49 +++++ .../ProductReviewConfiguration.cs | 51 +++++ .../copoilot-sample.DataAccess.csproj | 14 ++ .../DotnetCoreApi/appsettings.json | 13 ++ .../Contracts/ICategoryService.cs | 17 ++ .../Contracts/IProductService.cs | 17 ++ .../Controllers/CategoryController.cs | 81 ++++++++ .../Controllers/ProductAttributeController.cs | 74 +++++++ .../Controllers/ProductController.cs | 74 +++++++ .../copoilot-sample.Api/DBSetup.md | 172 +++++++++++++++ .../copoilot-sample.Api/InventoryDb.png | Bin 0 -> 146914 bytes .../Models/CategoryMappings.cs | 52 +++++ .../Models/Dtos/AddCategoryDto.cs | 13 ++ .../Models/Dtos/CategoryDto.cs | 11 + .../Models/Dtos/ProductAttributeDto.cs | 24 +++ .../Models/Dtos/ProductDto.cs | 37 ++++ .../copoilot-sample.Api/Program.cs | 42 ++++ .../Properties/launchSettings.json | 41 ++++ .../Services/CategoryService.cs | 101 +++++++++ .../Services/ProductAttributeService.cs | 89 ++++++++ .../Services/ProductService.cs | 123 +++++++++++ .../appsettings.Development.json | 8 + .../copoilot-sample.Api/appsettings.json | 12 ++ .../copoilot-sample.Api.csproj | 22 ++ .../copoilot-sample.Api/copoilot-sample.http | 6 + .../copoilot-sample.Api/copoilot.md | 45 ++++ .../copoilot-sample.Api/dbschema.sql | 62 ++++++ .../CategoryServiceTests.cs | 196 ++++++++++++++++++ .../copoilot-sample.Test.csproj | 31 +++ .../DotnetCoreApi/copoilot-sample.sln | 37 ++++ 41 files changed, 1843 insertions(+) create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/DataAccess/copoilot-sample.DataAccess.csproj create mode 100644 samples/SimpleFullStack/DotnetCoreApi/appsettings.json create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/ICategoryService.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/IProductService.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/CategoryController.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductAttributeController.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductController.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/InventoryDb.png create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/CategoryMappings.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/AddCategoryDto.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/CategoryDto.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductAttributeDto.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductDto.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Program.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Properties/launchSettings.json create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/CategoryService.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductAttributeService.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductService.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.Development.json create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.json create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/dbschema.sql create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/CategoryServiceTests.cs create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/copoilot-sample.Test.csproj create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.sln diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs new file mode 100644 index 0000000..8db5357 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore; +using copoilot_sample.DataAccess.Entities; +using copoilot_sample.DataAccess.EntityConfiguration; + +namespace copoilot_sample.DataAccess +{ + public class AppDbContext : DbContext + { + public AppDbContext(DbContextOptions options) : base(options) + { + } + + // Define DbSet properties for your entities + public DbSet Categories { get; set; } + public DbSet Products { get; set; } + public DbSet ProductPrices { get; set; } + public DbSet Inventory { get; set; } + public DbSet ProductAttributes { get; set; } + public DbSet ProductReviews { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // Apply entity configurations + modelBuilder.ApplyConfiguration(new CategoryConfiguration()); + modelBuilder.ApplyConfiguration(new ProductConfiguration()); + modelBuilder.ApplyConfiguration(new ProductPriceConfiguration()); + modelBuilder.ApplyConfiguration(new InventoryConfiguration()); + modelBuilder.ApplyConfiguration(new ProductAttributeConfiguration()); + modelBuilder.ApplyConfiguration(new ProductReviewConfiguration()); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs new file mode 100644 index 0000000..6a0babf --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs @@ -0,0 +1,15 @@ +namespace copoilot_sample.DataAccess.Entities +{ + public class Category + { + public int CategoryID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public int? ParentCategoryID { get; set; } + + // Navigation properties + public Category? ParentCategory { get; set; } + public ICollection? SubCategories { get; set; } + public ICollection? Products { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs new file mode 100644 index 0000000..8b0c0bb --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs @@ -0,0 +1,13 @@ +namespace copoilot_sample.DataAccess.Entities +{ + public class Inventory + { + public int InventoryID { get; set; } + public int ProductID { get; set; } + public int Quantity { get; set; } + public DateTime LastUpdated { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs new file mode 100644 index 0000000..705f859 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs @@ -0,0 +1,22 @@ +namespace copoilot_sample.DataAccess.Entities +{ + public class Product + { + public int ProductID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string SKU { get; set; } = null!; + public int CategoryID { get; set; } + public string? Brand { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime UpdatedAt { get; set; } + public bool IsActive { get; set; } + + // Navigation properties + public Category? Category { get; set; } + public ICollection? ProductPrices { get; set; } + public Inventory? Inventory { get; set; } + public ICollection? ProductAttributes { get; set; } + public ICollection? ProductReviews { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs new file mode 100644 index 0000000..ecbc4ff --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs @@ -0,0 +1,13 @@ +namespace copoilot_sample.DataAccess.Entities +{ + public class ProductAttribute + { + public int AttributeID { get; set; } + public int ProductID { get; set; } + public required string AttributeName { get; set; } + public required string AttributeValue { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs new file mode 100644 index 0000000..1a514a1 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs @@ -0,0 +1,15 @@ +namespace copoilot_sample.DataAccess.Entities +{ + public class ProductPrice + { + public int PriceID { get; set; } + public int ProductID { get; set; } + public decimal Price { get; set; } + public string CurrencyCode { get; set; } = "USD"; + public DateTime EffectiveFrom { get; set; } + public DateTime? EffectiveTill { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs new file mode 100644 index 0000000..03adfd8 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs @@ -0,0 +1,15 @@ +namespace copoilot_sample.DataAccess.Entities +{ + public class ProductReview + { + public int ReviewID { get; set; } + public int ProductID { get; set; } + public string? ReviewerName { get; set; } + public int Rating { get; set; } + public string? Comment { get; set; } + public DateTime ReviewDate { get; set; } + + // Navigation property + public Product? Product { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs new file mode 100644 index 0000000..7da4bf9 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs @@ -0,0 +1,41 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copoilot_sample.DataAccess.Entities; + +namespace copoilot_sample.DataAccess.EntityConfiguration +{ + public class CategoryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("Categories"); + + // Primary key + builder.HasKey(c => c.CategoryID); + + // Column mappings + builder.Property(c => c.CategoryID) + .HasColumnName("CategoryID") + .UseIdentityColumn(); + + builder.Property(c => c.Name) + .HasColumnName("Name") + .HasMaxLength(100) + .IsRequired(); + + builder.Property(c => c.Description) + .HasColumnName("Description") + .HasMaxLength(500); + + builder.Property(c => c.ParentCategoryID) + .HasColumnName("ParentCategoryID"); + + // Relationships + builder.HasOne(c => c.ParentCategory) + .WithMany(c => c.SubCategories) + .HasForeignKey(c => c.ParentCategoryID) + .OnDelete(DeleteBehavior.Restrict); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs new file mode 100644 index 0000000..129b42c --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copoilot_sample.DataAccess.Entities; + +namespace copoilot_sample.DataAccess.EntityConfiguration +{ + public class InventoryConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("Inventory"); + + // Primary key + builder.HasKey(i => i.InventoryID); + + // Column mappings + builder.Property(i => i.InventoryID) + .HasColumnName("InventoryID") + .UseIdentityColumn(); + + builder.Property(i => i.ProductID) + .HasColumnName("ProductID") + .IsRequired(); + + builder.Property(i => i.Quantity) + .HasColumnName("Quantity") + .IsRequired(); + + builder.Property(i => i.LastUpdated) + .HasColumnName("LastUpdated") + .HasDefaultValueSql("GETDATE()"); + + // Relationships + builder.HasOne(i => i.Product) + .WithOne(p => p.Inventory) + .HasForeignKey(i => i.ProductID); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs new file mode 100644 index 0000000..e3d1406 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copoilot_sample.DataAccess.Entities; + +namespace copoilot_sample.DataAccess.EntityConfiguration +{ + public class ProductAttributeConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("ProductAttributes"); + + // Primary key + builder.HasKey(pa => pa.AttributeID); + + // Column mappings + builder.Property(pa => pa.AttributeID) + .HasColumnName("AttributeID") + .UseIdentityColumn(); + + builder.Property(pa => pa.ProductID) + .HasColumnName("ProductID") + .IsRequired(); + + builder.Property(pa => pa.AttributeName) + .HasColumnName("AttributeName") + .HasMaxLength(100); + + builder.Property(pa => pa.AttributeValue) + .HasColumnName("AttributeValue") + .HasMaxLength(255); + + // Relationships + builder.HasOne(pa => pa.Product) + .WithMany(p => p.ProductAttributes) + .HasForeignKey(pa => pa.ProductID); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs new file mode 100644 index 0000000..84b17a8 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs @@ -0,0 +1,81 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copoilot_sample.DataAccess.Entities; + +namespace copoilot_sample.DataAccess.EntityConfiguration +{ + public class ProductConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("Products"); + + // Primary key + builder.HasKey(p => p.ProductID); + + // Column mappings + builder.Property(p => p.ProductID) + .HasColumnName("ProductID") + .UseIdentityColumn(); + + builder.Property(p => p.Name) + .HasColumnName("Name") + .HasMaxLength(200) + .IsRequired(); + + builder.Property(p => p.Description) + .HasColumnName("Description") + .HasColumnType("NVARCHAR(MAX)"); + + builder.Property(p => p.SKU) + .HasColumnName("SKU") + .HasMaxLength(100) + .IsRequired(); + + builder.HasIndex(p => p.SKU) + .IsUnique(); + + builder.Property(p => p.CategoryID) + .HasColumnName("CategoryID") + .IsRequired(); + + builder.Property(p => p.Brand) + .HasColumnName("Brand") + .HasMaxLength(100); + + builder.Property(p => p.CreatedAt) + .HasColumnName("CreatedAt") + .HasDefaultValueSql("GETDATE()"); + + builder.Property(p => p.UpdatedAt) + .HasColumnName("UpdatedAt") + .HasDefaultValueSql("GETDATE()"); + + builder.Property(p => p.IsActive) + .HasColumnName("IsActive") + .HasDefaultValue(true); + + // Relationships + builder.HasOne(p => p.Category) + .WithMany(c => c.Products) + .HasForeignKey(p => p.CategoryID); + + builder.HasMany(p => p.ProductPrices) + .WithOne(pp => pp.Product) + .HasForeignKey(pp => pp.ProductID); + + builder.HasOne(p => p.Inventory) + .WithOne(i => i.Product) + .HasForeignKey(i => i.ProductID); + + builder.HasMany(p => p.ProductAttributes) + .WithOne(pa => pa.Product) + .HasForeignKey(pa => pa.ProductID); + + builder.HasMany(p => p.ProductReviews) + .WithOne(pr => pr.Product) + .HasForeignKey(pr => pr.ProductID); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs new file mode 100644 index 0000000..4e747e0 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs @@ -0,0 +1,49 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copoilot_sample.DataAccess.Entities; + +namespace copoilot_sample.DataAccess.EntityConfiguration +{ + public class ProductPriceConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("ProductPrices"); + + // Primary key + builder.HasKey(pp => pp.PriceID); + + // Column mappings + builder.Property(pp => pp.PriceID) + .HasColumnName("PriceID") + .UseIdentityColumn(); + + builder.Property(pp => pp.ProductID) + .HasColumnName("ProductID") + .IsRequired(); + + builder.Property(pp => pp.Price) + .HasColumnName("Price") + .HasColumnType("DECIMAL(18, 2)") + .IsRequired(); + + builder.Property(pp => pp.CurrencyCode) + .HasColumnName("CurrencyCode") + .HasMaxLength(3) + .HasDefaultValue("USD"); + + builder.Property(pp => pp.EffectiveFrom) + .HasColumnName("EffectiveFrom") + .HasDefaultValueSql("GETDATE()"); + + builder.Property(pp => pp.EffectiveTill) + .HasColumnName("EffectiveTill"); + + // Relationships + builder.HasOne(pp => pp.Product) + .WithMany(p => p.ProductPrices) + .HasForeignKey(pp => pp.ProductID); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs new file mode 100644 index 0000000..1dc23f5 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using copoilot_sample.DataAccess.Entities; + +namespace copoilot_sample.DataAccess.EntityConfiguration +{ + public class ProductReviewConfiguration : IEntityTypeConfiguration + { + public void Configure(EntityTypeBuilder builder) + { + // Table mapping + builder.ToTable("ProductReviews"); + + // Primary key + builder.HasKey(pr => pr.ReviewID); + + // Column mappings + builder.Property(pr => pr.ReviewID) + .HasColumnName("ReviewID") + .UseIdentityColumn(); + + builder.Property(pr => pr.ProductID) + .HasColumnName("ProductID") + .IsRequired(); + + builder.Property(pr => pr.ReviewerName) + .HasColumnName("ReviewerName") + .HasMaxLength(100); + + builder.Property(pr => pr.Rating) + .HasColumnName("Rating") + .IsRequired(); + + // Add check constraint for Rating + builder.HasCheckConstraint("CK_ProductReviews_Rating", "Rating BETWEEN 1 AND 5"); + + builder.Property(pr => pr.Comment) + .HasColumnName("Comment") + .HasColumnType("NVARCHAR(MAX)"); + + builder.Property(pr => pr.ReviewDate) + .HasColumnName("ReviewDate") + .HasDefaultValueSql("GETDATE()"); + + // Relationships + builder.HasOne(pr => pr.Product) + .WithMany(p => p.ProductReviews) + .HasForeignKey(pr => pr.ProductID); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/copoilot-sample.DataAccess.csproj b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/copoilot-sample.DataAccess.csproj new file mode 100644 index 0000000..dfa4505 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/copoilot-sample.DataAccess.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/samples/SimpleFullStack/DotnetCoreApi/appsettings.json b/samples/SimpleFullStack/DotnetCoreApi/appsettings.json new file mode 100644 index 0000000..1142993 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/appsettings.json @@ -0,0 +1,13 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=YourDatabaseName;Trusted_Connection=True;TrustServerCertificate=True;" + }, + "AllowedHosts": "*" +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/ICategoryService.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/ICategoryService.cs new file mode 100644 index 0000000..c369743 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/ICategoryService.cs @@ -0,0 +1,17 @@ +using copoilot_sample.Api.Models.Dtos; + +namespace copoilot_sample.Api.Services +{ + /// + /// Interface for managing categories in the application. + /// Provides methods for CRUD operations on categories. + /// + public interface ICategoryService + { + Task> GetCategoriesAsync(); + Task GetCategoryByIdAsync(int id); + Task AddCategoryAsync(AddCategoryDto addCategoryDto); + Task UpdateCategoryDescriptionAsync(int id, UpdateCategoryDescriptionDto updateDto); + Task DeleteCategoryAsync(int id); + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/IProductService.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/IProductService.cs new file mode 100644 index 0000000..7009486 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/IProductService.cs @@ -0,0 +1,17 @@ +using copoilot_sample.Api.Models.Dtos; + +namespace copoilot_sample.Api.Services +{ + /// + /// Interface for managing products in the application. + /// Provides methods for CRUD operations on products. + /// + public interface IProductService + { + Task> GetProductsAsync(); + Task GetProductByIdAsync(int id); + Task AddProductAsync(AddProductDto addProductDto); + Task UpdateProductAsync(int id, UpdateProductDto updateProductDto); + Task DeleteProductAsync(int id); + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/CategoryController.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/CategoryController.cs new file mode 100644 index 0000000..9d41e69 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/CategoryController.cs @@ -0,0 +1,81 @@ +using copoilot_sample.Api.Models.Dtos; +using copoilot_sample.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace copoilot_sample.Api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class CategoryController : ControllerBase + { + private readonly ICategoryService _categoryService; + + public CategoryController(ICategoryService categoryService) + { + _categoryService = categoryService; + } + + [HttpGet] + public async Task GetCategories() + { + var categories = await _categoryService.GetCategoriesAsync(); + return Ok(categories); + } + + [HttpGet("{id}")] + public async Task GetCategoryById(int id) + { + var category = await _categoryService.GetCategoryByIdAsync(id); + if (category == null) + { + return NotFound(new { Message = $"Category with ID {id} not found." }); + } + return Ok(category); + } + + [HttpPost] + public async Task AddCategory([FromBody] AddCategoryDto addCategoryDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var category = await _categoryService.AddCategoryAsync(addCategoryDto); + if (category == null) + { + return Conflict(new { Message = $"A category with {addCategoryDto.Name} already exists." }); + } + return CreatedAtAction(nameof(GetCategoryById), new { id = category.CategoryID }, category); + } + + [HttpPatch("{id}/description")] + public async Task UpdateCategoryDescription(int id, [FromBody] UpdateCategoryDescriptionDto updateDto) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + var success = await _categoryService.UpdateCategoryDescriptionAsync(id, updateDto); + if (!success) + { + return NotFound(new { Message = $"Category with ID {id} not found." }); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteCategory(int id) + { + var success = await _categoryService.DeleteCategoryAsync(id); + if (!success) + { + return NotFound(new { Message = $"Category with ID {id} not found." }); + } + + return NoContent(); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductAttributeController.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductAttributeController.cs new file mode 100644 index 0000000..a34ba4c --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductAttributeController.cs @@ -0,0 +1,74 @@ +using copoilot_sample.Api.Models.Dtos; +using copoilot_sample.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace copoilot_sample.Api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class ProductAttributeController : ControllerBase + { + private readonly ProductAttributeService _productAttributeService; + + public ProductAttributeController(ProductAttributeService productAttributeService) + { + _productAttributeService = productAttributeService; + } + + [HttpGet] + public async Task GetProductAttributes() + { + var attributes = await _productAttributeService.GetProductAttributesAsync(); + return Ok(attributes); + } + + [HttpGet("{id}")] + public async Task GetProductAttributeById(int id) + { + var attribute = await _productAttributeService.GetProductAttributeByIdAsync(id); + if (attribute == null) + { + return NotFound(new { Message = $"Product Attribute with ID {id} not found." }); + } + return Ok(attribute); + } + + [HttpPost] + public async Task AddProductAttribute([FromBody] AddProductAttributeDto addDto) + { + try + { + var attribute = await _productAttributeService.AddProductAttributeAsync(addDto); + return CreatedAtAction(nameof(GetProductAttributeById), new { id = attribute.AttributeID }, attribute); + } + catch (ArgumentException ex) + { + return BadRequest(new { Message = ex.Message }); + } + } + + [HttpPut("{id}")] + public async Task UpdateProductAttribute(int id, [FromBody] UpdateProductAttributeDto updateDto) + { + var success = await _productAttributeService.UpdateProductAttributeAsync(id, updateDto); + if (!success) + { + return NotFound(new { Message = $"Product Attribute with ID {id} not found." }); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteProductAttribute(int id) + { + var success = await _productAttributeService.DeleteProductAttributeAsync(id); + if (!success) + { + return NotFound(new { Message = $"Product Attribute with ID {id} not found." }); + } + + return NoContent(); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductController.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductController.cs new file mode 100644 index 0000000..7c7d730 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductController.cs @@ -0,0 +1,74 @@ +using copoilot_sample.Api.Models.Dtos; +using copoilot_sample.Api.Services; +using Microsoft.AspNetCore.Mvc; + +namespace copoilot_sample.Api.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class ProductController : ControllerBase + { + private readonly IProductService _productService; + + public ProductController(IProductService productService) + { + _productService = productService; + } + + [HttpGet] + public async Task GetProducts() + { + var products = await _productService.GetProductsAsync(); + return Ok(products); + } + + [HttpGet("{id}")] + public async Task GetProductById(int id) + { + var product = await _productService.GetProductByIdAsync(id); + if (product == null) + { + return NotFound(new { Message = $"Product with ID {id} not found." }); + } + return Ok(product); + } + + [HttpPost] + public async Task AddProduct([FromBody] AddProductDto addProductDto) + { + try + { + var product = await _productService.AddProductAsync(addProductDto); + return CreatedAtAction(nameof(GetProductById), new { id = product.ProductID }, product); + } + catch (ArgumentException ex) + { + return BadRequest(new { Message = "Invalid request.", Details = ex.Message }); + } + } + + [HttpPut("{id}")] + public async Task UpdateProduct(int id, [FromBody] UpdateProductDto updateProductDto) + { + var success = await _productService.UpdateProductAsync(id, updateProductDto); + if (!success) + { + return NotFound(new { Message = $"Product with ID {id} not found." }); + } + + return NoContent(); + } + + [HttpDelete("{id}")] + public async Task DeleteProduct(int id) + { + var success = await _productService.DeleteProductAsync(id); + if (!success) + { + return NotFound(new { Message = $"Product with ID {id} not found." }); + } + + return NoContent(); + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md new file mode 100644 index 0000000..65e8606 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md @@ -0,0 +1,172 @@ +# Database Setup + +1. Need a MsSql DB in localhost. download and install Developer or Express versions. Refer https://www.microsoft.com/en-us/sql-server/sql-server-downloads +2. Create a database named "Inventory" +3. Run below DDL statements to create necessary tables + +``` +-- Inventory Database schema +--Categories Table +CREATE TABLE Categories ( + CategoryID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(100) NOT NULL, + Description NVARCHAR(500), + ParentCategoryID INT NULL, + FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID) +); + +--Products Table +CREATE TABLE Products ( + ProductID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(200) NOT NULL, + Description NVARCHAR(MAX), + SKU NVARCHAR(100) UNIQUE NOT NULL, + CategoryID INT NOT NULL, + Brand NVARCHAR(100), + CreatedAt DATETIME DEFAULT GETDATE(), + UpdatedAt DATETIME DEFAULT GETDATE(), + IsActive BIT DEFAULT 1, + FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID) +); + +--Pricing Table +CREATE TABLE ProductPrices ( + PriceID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Price DECIMAL(18, 2) NOT NULL, + CurrencyCode CHAR(3) DEFAULT 'USD', + EffectiveFrom DATETIME DEFAULT GETDATE(), + EffectiveTill DATETIME NULL, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Inventory Table +CREATE TABLE Inventory ( + InventoryID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Quantity INT NOT NULL, + LastUpdated DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Product Attributes +CREATE TABLE ProductAttributes ( + AttributeID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + AttributeName NVARCHAR(100), + AttributeValue NVARCHAR(255), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Product Reviews +CREATE TABLE ProductReviews ( + ReviewID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + ReviewerName NVARCHAR(100), + Rating INT CHECK (Rating BETWEEN 1 AND 5), + Comment NVARCHAR(MAX), + ReviewDate DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +``` + +4. Run below DML statements to insert some sample data + +``` +-- Insert Categories +INSERT INTO Categories (Name, Description, ParentCategoryID) VALUES +('Electronics', 'Electronic gadgets and devices', NULL), +('Laptops', 'Portable computers', 1), +('Smartphones', 'Mobile phones and accessories', 1), +('Accessories', 'Electronics accessories', 1); + +-- Insert Products +INSERT INTO Products (Name, Description, SKU, CategoryID, Brand) VALUES +('UltraBook X1', 'Lightweight business laptop with high performance', 'SKU-UBX1', 2, 'TechBrand'), +('Gaming Beast Z9', 'High-end gaming laptop with RGB lighting', 'SKU-GBZ9', 2, 'GamePro'), +('Galaxy X10', 'Latest smartphone with AI-powered camera', 'SKU-GX10', 3, 'SmartTech'), +('EarPods Pro', 'Wireless earphones with noise cancellation', 'SKU-EPP', 4, 'SoundWave'); + +-- Insert Product Prices +INSERT INTO ProductPrices (ProductID, Price, CurrencyCode, EffectiveFrom, EffectiveTill) VALUES +(1, 1299.99, 'USD', '2025-01-01', NULL), +(1, 1199.99, 'USD', '2024-10-01', '2024-12-31'), +(2, 1999.00, 'USD', '2025-01-15', NULL), +(3, 899.50, 'USD', '2025-03-15', NULL), +(4, 199.99, 'USD', '2025-02-01', NULL); + +-- Insert Inventory +INSERT INTO Inventory (ProductID, Quantity) VALUES +(1, 25), +(2, 10), +(3, 50), +(4, 100); + +-- Insert Product Attributes +INSERT INTO ProductAttributes (ProductID, AttributeName, AttributeValue) VALUES +(1, 'Processor', 'Intel Core i7'), +(1, 'RAM', '16GB'), +(1, 'Storage', '512GB SSD'), +(2, 'Processor', 'AMD Ryzen 9'), +(2, 'RAM', '32GB'), +(2, 'Graphics Card', 'NVIDIA RTX 4080'), +(3, 'Display', '6.5-inch OLED'), +(3, 'Battery', '4000mAh'), +(3, 'Camera', '108MP'), +(4, 'Connectivity', 'Bluetooth 5.2'), +(4, 'Noise Cancellation', 'Active'), +(4, 'Battery Life', '8 hours'); + +-- Insert Product Reviews +INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES +(1, 'Alice Johnson', 5, 'Absolutely love this laptop! Fast and sleek.'), +(1, 'Bob Smith', 4, 'Good performance but gets a bit warm.'), +(2, 'Tommy Lee', 5, 'A beast for gaming. Smooth experience!'), +(3, 'Carlos Vega', 5, 'Amazing camera and display! Worth every penny.'), +(3, 'Diana Lee', 3, 'Battery life could be better.'), +(4, 'Emma Stone', 4, 'Very comfortable fit and great sound.'), +(4, 'John Doe', 2, 'Connection drops sometimes.'); + + +-- More Products +INSERT INTO Products (Name, Description, SKU, CategoryID, Brand) VALUES +('ThinkMate Pro 14', 'Durable business laptop with excellent battery life', 'SKU-TMP14', 2, 'ThinkCorp'), +('PixelCam A2', 'Compact smartphone with high-resolution camera', 'SKU-PXA2', 3, 'PixelTech'), +('PowerCharge 10000', 'Portable power bank with fast charging support', 'SKU-PC10000', 4, 'ChargeX'); + +-- More Product Prices +INSERT INTO ProductPrices (ProductID, Price, CurrencyCode, EffectiveFrom, EffectiveTill) VALUES +(5, 999.99, 'USD', '2025-04-01', NULL), +(6, 649.00, 'USD', '2025-04-15', NULL), +(7, 49.99, 'USD', '2025-03-01', NULL); + +-- More Inventory +INSERT INTO Inventory (ProductID, Quantity) VALUES +(5, 30), +(6, 45), +(7, 200); + +-- More Product Attributes +INSERT INTO ProductAttributes (ProductID, AttributeName, AttributeValue) VALUES +(5, 'Processor', 'Intel i5'), +(5, 'RAM', '8GB'), +(5, 'Weight', '1.3kg'), +(6, 'Camera', '64MP'), +(6, 'Storage', '128GB'), +(6, 'Display', '6.1-inch AMOLED'), +(7, 'Capacity', '10000mAh'), +(7, 'USB Ports', '2'), +(7, 'Fast Charging', 'Yes'); + +-- More Product Reviews +INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES +(5, 'Harvey Dent', 4, 'Reliable laptop, solid build and decent performance.'), +(5, 'Rachel Green', 3, 'Good for light work, but a bit slow for multitasking.'), +(6, 'Bruce Banner', 5, 'Compact yet powerful. Great value for the price.'), +(6, 'Natasha Romanoff', 4, 'Excellent camera quality and battery.'), +(7, 'Steve Rogers', 5, 'Lasts all day. Perfect for travel.'), +(7, 'Tony Stark', 3, 'Charges fast, but gets warm during use.'); + + +``` diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/InventoryDb.png b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/InventoryDb.png new file mode 100644 index 0000000000000000000000000000000000000000..ae6220296585717648735fdc148ed2b480e38835 GIT binary patch literal 146914 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 z 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

m$gD79 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 ?c(isxG-Q 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 _compiledMapper = null!; + + static CategoryMappings() + { + _compiledMapper = MapCategory; + } + + private static CategoryDto MapCategory(Category category) + { + return new CategoryDto + { + CategoryID = category.CategoryID, + Name = category.Name, + Description = category.Description, + ParentCategoryID = category.ParentCategoryID, + SubCategories = category.SubCategories?.Select(sc => MapCategory(sc)).ToList() + }; + } + + ///

+ /// Expression to map a Category entity to a CategoryDto without subcategories. + /// + private static readonly Expression> ToCategoryDto = category => new CategoryDto + { + CategoryID = category.CategoryID, + Name = category.Name, + Description = category.Description, + ParentCategoryID = category.ParentCategoryID, + SubCategories = null // Subcategories will be mapped separately + }; + + /// + /// Method to map subcategories after the main query is executed. + /// + public static CategoryDto MapWithSubcategories(Category category) + { + var dto = _compiledMapper(category); + if (category.SubCategories != null) + { + dto.SubCategories = category.SubCategories.Select(MapWithSubcategories).ToList(); + } + return dto; + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/AddCategoryDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/AddCategoryDto.cs new file mode 100644 index 0000000..09380f4 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/AddCategoryDto.cs @@ -0,0 +1,13 @@ +namespace copoilot_sample.Api.Models.Dtos +{ + public class AddCategoryDto + { + public string Name { get; set; } = null!; + public string? Description { get; set; } + public int? ParentCategoryID { get; set; } + } + public class UpdateCategoryDescriptionDto + { + public string? Description { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/CategoryDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/CategoryDto.cs new file mode 100644 index 0000000..b2ca556 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/CategoryDto.cs @@ -0,0 +1,11 @@ +namespace copoilot_sample.Api.Models.Dtos +{ + public class CategoryDto + { + public int CategoryID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public int? ParentCategoryID { get; set; } + public List? SubCategories { get; set; } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductAttributeDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductAttributeDto.cs new file mode 100644 index 0000000..1d3d3b4 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductAttributeDto.cs @@ -0,0 +1,24 @@ +namespace copoilot_sample.Api.Models.Dtos +{ + public class ProductAttributeDto + { + public int AttributeID { get; set; } + public int ProductID { get; set; } + public string AttributeName { get; set; } = null!; + public string AttributeValue { get; set; } = null!; + } + + public class AddProductAttributeDto + { + public int ProductID { get; set; } + public string AttributeName { get; set; } = null!; + public string AttributeValue { get; set; } = null!; + } + + public class UpdateProductAttributeDto + { + public required string AttributeName { get; set; } + public required string AttributeValue { get; set; } + } + +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductDto.cs new file mode 100644 index 0000000..c511adf --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductDto.cs @@ -0,0 +1,37 @@ +namespace copoilot_sample.Api.Models.Dtos +{ + public class ProductDto + { + public int ProductID { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string SKU { get; set; } = null!; + public int CategoryID { get; set; } + public string? Brand { get; set; } + public bool IsActive { get; set; } + + public CategoryDto Category { get; set; } + } + + public class AddProductDto + { + public string Name { get; set; } = null!; + public string? Description { get; set; } + public string SKU { get; set; } = null!; + public int CategoryID { get; set; } + public string? Brand { get; set; } + public bool IsActive { get; set; } + } + + public class UpdateProductDto + { + public string? Name { get; set; } + public string? Description { get; set; } + public string? SKU { get; set; } + public int? CategoryID { get; set; } + public string? Brand { get; set; } + public bool? IsActive { get; set; } + } + + +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Program.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Program.cs new file mode 100644 index 0000000..8b54705 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Program.cs @@ -0,0 +1,42 @@ +using copoilot_sample.DataAccess; +using copoilot_sample.Api.Services; +using Microsoft.EntityFrameworkCore; +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +builder.Services.AddControllers().AddJsonOptions(options => +{ + options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; +}); +; + +// Register CategoryService +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +// Configure Entity Framework Core with SQL Server +builder.Services.AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); + +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Properties/launchSettings.json b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Properties/launchSettings.json new file mode 100644 index 0000000..0938567 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:43099", + "sslPort": 44395 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5147", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7111;http://localhost:5147", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/CategoryService.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/CategoryService.cs new file mode 100644 index 0000000..a27c663 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/CategoryService.cs @@ -0,0 +1,101 @@ +using copoilot_sample.DataAccess; +using copoilot_sample.DataAccess.Entities; +using copoilot_sample.Api.Models.Dtos; +using Microsoft.EntityFrameworkCore; + +namespace copoilot_sample.Api.Services +{ + /// + /// Service for managing categories in the application. + /// Provides methods for CRUD operations on categories. + /// + public class CategoryService : ICategoryService + { + private readonly AppDbContext _dbContext; + + /// + /// Initializes a new instance of the class. + /// + /// The database context to interact with the database. + public CategoryService(AppDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetCategoriesAsync() + { + return await _dbContext.Categories + .Include(c => c.SubCategories) + .Select(i => CategoryMappings.MapWithSubcategories(i)) + .ToListAsync(); + } + + public async Task GetCategoryByIdAsync(int id) + { + var category = await _dbContext.Categories + .Include(c => c.SubCategories) + .FirstOrDefaultAsync(c => c.CategoryID == id); + + if (category == null) + { + return null; + } + + // Use the mapping expression to map the category to a CategoryDto + return CategoryMappings.MapWithSubcategories(category); + } + + public async Task AddCategoryAsync(AddCategoryDto addCategoryDto) + { + var existingCategory = await _dbContext.Categories + .FirstOrDefaultAsync(c => c.Name.ToLower() == addCategoryDto.Name.ToLower()); + + if (existingCategory != null) + { + return null; + } + + var category = new Category + { + Name = addCategoryDto.Name, + Description = addCategoryDto.Description, + ParentCategoryID = addCategoryDto.ParentCategoryID + }; + + _dbContext.Categories.Add(category); + await _dbContext.SaveChangesAsync(); + + // Use the mapping expression to map the newly created category to a CategoryDto + return CategoryMappings.MapWithSubcategories(category); + } + + public async Task UpdateCategoryDescriptionAsync(int id, UpdateCategoryDescriptionDto updateDto) + { + var category = await _dbContext.Categories.FindAsync(id); + if (category == null) + { + return false; + } + + category.Description = updateDto.Description; + _dbContext.Categories.Update(category); + await _dbContext.SaveChangesAsync(); + + return true; + } + + public async Task DeleteCategoryAsync(int id) + { + var category = await _dbContext.Categories.FindAsync(id); + if (category == null) + { + return false; + } + + _dbContext.Categories.Remove(category); + await _dbContext.SaveChangesAsync(); + + return true; + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductAttributeService.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductAttributeService.cs new file mode 100644 index 0000000..be8387c --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductAttributeService.cs @@ -0,0 +1,89 @@ +using copoilot_sample.DataAccess; +using copoilot_sample.DataAccess.Entities; +using copoilot_sample.Api.Models.Dtos; +using Microsoft.EntityFrameworkCore; + +namespace copoilot_sample.Api.Services +{ + public class ProductAttributeService + { + private readonly AppDbContext _dbContext; + + public ProductAttributeService(AppDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetProductAttributesAsync() + { + var attributes = await _dbContext.ProductAttributes.ToListAsync(); + return attributes.Select(a => new ProductAttributeDto + { + AttributeID = a.AttributeID, + ProductID = a.ProductID, + AttributeName = a.AttributeName, + AttributeValue = a.AttributeValue + }).ToList(); + } + + public async Task GetProductAttributeByIdAsync(int id) + { + var attribute = await _dbContext.ProductAttributes.FindAsync(id); + if (attribute == null) return null; + + return new ProductAttributeDto + { + AttributeID = attribute.AttributeID, + ProductID = attribute.ProductID, + AttributeName = attribute.AttributeName, + AttributeValue = attribute.AttributeValue + }; + } + + public async Task AddProductAttributeAsync(AddProductAttributeDto addDto) + { + var product = await _dbContext.Products.FindAsync(addDto.ProductID); + if (product == null) + { + throw new ArgumentException($"Product with ID {addDto.ProductID} does not exist."); + } + + var attribute = new ProductAttribute + { + ProductID = addDto.ProductID, + AttributeName = addDto.AttributeName, + AttributeValue = addDto.AttributeValue + }; + + _dbContext.ProductAttributes.Add(attribute); + await _dbContext.SaveChangesAsync(); + + return attribute; + } + + public async Task UpdateProductAttributeAsync(int id, UpdateProductAttributeDto updateDto) + { + var attribute = await _dbContext.ProductAttributes.FindAsync(id); + if (attribute == null) return false; + + attribute.AttributeName = updateDto.AttributeName; + attribute.AttributeValue = updateDto.AttributeValue; + + _dbContext.ProductAttributes.Update(attribute); + await _dbContext.SaveChangesAsync(); + + return true; + } + + public async Task DeleteProductAttributeAsync(int id) + { + var attribute = await _dbContext.ProductAttributes.FindAsync(id); + if (attribute == null) return false; + + _dbContext.ProductAttributes.Remove(attribute); + await _dbContext.SaveChangesAsync(); + + return true; + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductService.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductService.cs new file mode 100644 index 0000000..bac3192 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductService.cs @@ -0,0 +1,123 @@ +using copoilot_sample.Api.Models.Dtos; +using copoilot_sample.DataAccess; +using copoilot_sample.DataAccess.Entities; +using Microsoft.EntityFrameworkCore; + +namespace copoilot_sample.Api.Services +{ + /// + /// Service for managing products in the application. + /// Provides methods for CRUD operations on products. + /// + public class ProductService : IProductService + { + private readonly AppDbContext _dbContext; + + /// + /// Initializes a new instance of the class. + /// + /// The database context to interact with the database. + public ProductService(AppDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetProductsAsync() + { + var products = await _dbContext.Products.ToListAsync(); + return products.Select(p => new ProductDto + { + ProductID = p.ProductID, + Name = p.Name, + Description = p.Description, + SKU = p.SKU, + CategoryID = p.CategoryID, + Brand = p.Brand, + IsActive = p.IsActive, + Category = new CategoryDto + { + CategoryID = p.Category.CategoryID, + Name = p.Category.Name, + Description = p.Category.Description, + + } + }).ToList(); + } + + public async Task GetProductByIdAsync(int id) + { + var product = await _dbContext.Products.FindAsync(id); + if (product == null) return null; + + return new ProductDto + { + ProductID = product.ProductID, + Name = product.Name, + Description = product.Description, + SKU = product.SKU, + CategoryID = product.CategoryID, + Brand = product.Brand, + IsActive = product.IsActive + }; + } + + public async Task AddProductAsync(AddProductDto addProductDto) + { + var product = new Product + { + Name = addProductDto.Name, + Description = addProductDto.Description, + SKU = addProductDto.SKU, + CategoryID = addProductDto.CategoryID, + Brand = addProductDto.Brand, + IsActive = addProductDto.IsActive, + CreatedAt = DateTime.UtcNow, + UpdatedAt = DateTime.UtcNow + }; + + _dbContext.Products.Add(product); + await _dbContext.SaveChangesAsync(); + + return new ProductDto + { + ProductID = product.ProductID, + Name = product.Name, + Description = product.Description, + SKU = product.SKU, + CategoryID = product.CategoryID, + Brand = product.Brand, + IsActive = product.IsActive + }; + } + + public async Task UpdateProductAsync(int id, UpdateProductDto updateProductDto) + { + var product = await _dbContext.Products.FindAsync(id); + if (product == null) return false; + + product.Name = updateProductDto.Name ?? product.Name; + product.Description = updateProductDto.Description ?? product.Description; + product.SKU = updateProductDto.SKU ?? product.SKU; + product.CategoryID = updateProductDto.CategoryID ?? product.CategoryID; + product.Brand = updateProductDto.Brand ?? product.Brand; + product.IsActive = updateProductDto.IsActive ?? product.IsActive; + product.UpdatedAt = DateTime.UtcNow; + + _dbContext.Products.Update(product); + await _dbContext.SaveChangesAsync(); + + return true; + } + + public async Task DeleteProductAsync(int id) + { + var product = await _dbContext.Products.FindAsync(id); + if (product == null) return false; + + _dbContext.Products.Remove(product); + await _dbContext.SaveChangesAsync(); + + return true; + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.Development.json b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.json b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.json new file mode 100644 index 0000000..e3806cb --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.json @@ -0,0 +1,12 @@ +{ + "ConnectionStrings": { + "DefaultConnection": "Server=localhost;Database=Inventory;Trusted_Connection=True;TrustServerCertificate=True;" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj new file mode 100644 index 0000000..ee8e8fb --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + enable + copoilot_sample + + + + + + + + + + + + + + + diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http new file mode 100644 index 0000000..e8c7cea --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http @@ -0,0 +1,6 @@ +@copoilot_sample_HostAddress = http://localhost:5147 + +GET {{copoilot_sample_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md new file mode 100644 index 0000000..cfd413f --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md @@ -0,0 +1,45 @@ +# Copoilot chat +## Ask mode in Copilot +1. Explain the code: + ``` + @workspace , Explain the DataAccess project in the solution. Also explain how is it being used and triggerred + ``` +2. Ask copoilot to implement a feature: + ``` + @workspace, In ProductService class, AddProductAsync method, add a check to see if the product already exists in the database. If it does, throw a custom exception named ProductAlreadyExistsException. + ``` +3. Ask copilot to implement a feature: + ``` + @workspace , Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle db operations and add it to DI. + Also add necessary Dto models. + ``` +4. Ask copoilot about an exception: + ``` + Run GetProducts end point and check if there are any exceptions, and explore "Analyze with copoilot" + ``` + +## Edit Mode in Copoilot +1. Document the code: + ``` + @workspace , Document the productAttributeService class and its methods. + ``` +2. Implement a feature: + ``` + @workspace , Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle db operations and add it to DI. + Also add necessary Dto models. + ``` +3. Create unit tests for the code: + ``` + @workspace , Create unit tests for the ProductAttributeService class and its methods. + ``` + +# Inline chat with copoilot +1. Explain about a selected code: + +2. Ask copoilot to write code: + ``` + In ProductService class, AddProductAsync method, select the method body and click "Alt + /" to open inline chat. + Please add a check if teh Category already exists in the database. If it does, throw a exception. + ``` + + \ No newline at end of file diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/dbschema.sql b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/dbschema.sql new file mode 100644 index 0000000..fceb5b0 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/dbschema.sql @@ -0,0 +1,62 @@ +--Categories Table +CREATE TABLE Categories ( + CategoryID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(100) NOT NULL, + Description NVARCHAR(500), + ParentCategoryID INT NULL, + FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID) +); + +--Products Table +CREATE TABLE Products ( + ProductID INT PRIMARY KEY IDENTITY(1,1), + Name NVARCHAR(200) NOT NULL, + Description NVARCHAR(MAX), + SKU NVARCHAR(100) UNIQUE NOT NULL, + CategoryID INT NOT NULL, + Brand NVARCHAR(100), + CreatedAt DATETIME DEFAULT GETDATE(), + UpdatedAt DATETIME DEFAULT GETDATE(), + IsActive BIT DEFAULT 1, + FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID) +); + +--Pricing Table +CREATE TABLE ProductPrices ( + PriceID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Price DECIMAL(18, 2) NOT NULL, + CurrencyCode CHAR(3) DEFAULT 'USD', + EffectiveFrom DATETIME DEFAULT GETDATE(), + EffectiveTill DATETIME NULL, + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Inventory Table +CREATE TABLE Inventory ( + InventoryID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + Quantity INT NOT NULL, + LastUpdated DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Product Attributes +CREATE TABLE ProductAttributes ( + AttributeID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + AttributeName NVARCHAR(100), + AttributeValue NVARCHAR(255), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); + +--Product Reviews +CREATE TABLE ProductReviews ( + ReviewID INT PRIMARY KEY IDENTITY(1,1), + ProductID INT NOT NULL, + ReviewerName NVARCHAR(100), + Rating INT CHECK (Rating BETWEEN 1 AND 5), + Comment NVARCHAR(MAX), + ReviewDate DATETIME DEFAULT GETDATE(), + FOREIGN KEY (ProductID) REFERENCES Products(ProductID) +); diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/CategoryServiceTests.cs b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/CategoryServiceTests.cs new file mode 100644 index 0000000..c97d435 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/CategoryServiceTests.cs @@ -0,0 +1,196 @@ +using copoilot_sample.DataAccess; +using copoilot_sample.DataAccess.Entities; +using copoilot_sample.Api.Models.Dtos; +using copoilot_sample.Api.Services; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; + +public class CategoryServiceTests +{ + private AppDbContext GetInMemoryDbContext() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "TestDatabase") + .Options; + + return new AppDbContext(options); + } + + [Fact] + public async Task GetCategoriesAsync_ShouldReturnAllCategories() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.AddRange(new List + { + new Category { CategoryID = 100, Name = "Electronics", Description = "Electronic items" }, + new Category { CategoryID = 200, Name = "Books", Description = "Books and novels" } + }); + await dbContext.SaveChangesAsync(); + var total = dbContext.Categories.Count(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.GetCategoriesAsync(); + + // Assert + result.Should().HaveCount(total); + result.Should().Contain(c => c.Name == "Electronics"); + result.Should().Contain(c => c.Name == "Books"); + } + + [Fact] + public async Task GetCategoryByIdAsync_ShouldReturnCategory_WhenCategoryExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 1, Name = "Electronics", Description = "Electronic items" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.GetCategoryByIdAsync(1); + + // Assert + result.Should().NotBeNull(); + result!.Name.Should().Be("Electronics"); + } + + [Fact] + public async Task GetCategoryByIdAsync_ShouldReturnNull_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.GetCategoryByIdAsync(99); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public async Task AddCategoryAsync_ShouldAddCategory_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + var categoryService = new CategoryService(dbContext); + + var newCategory = new AddCategoryDto + { + Name = "Electronics Apps", + Description = "Electronic items" + }; + var totalCount = dbContext.Categories.Count(); + + // Act + var result = await categoryService.AddCategoryAsync(newCategory); + + // Assert + result.Should().NotBeNull(); + result.Name.Should().Be("Electronics Apps"); + (await dbContext.Categories.CountAsync()).Should().Be(totalCount +1); + } + + [Fact] + public async Task AddCategoryAsync_ShouldReturnNull_WhenCategoryWithSameNameExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 101, Name = "Electronics101", Description = "Electronic items" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + var newCategory = new AddCategoryDto + { + Name = "Electronics101", + Description = "Duplicate category" + }; + + // Act + var result = await categoryService.AddCategoryAsync(newCategory); + + // Assert + result.Should().BeNull(); + } + + [Fact] + public async Task UpdateCategoryDescriptionAsync_ShouldUpdateDescription_WhenCategoryExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 12, Name = "Electronics12", Description = "Old description" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + var updateDto = new UpdateCategoryDescriptionDto + { + Description = "Updated description" + }; + + // Act + var result = await categoryService.UpdateCategoryDescriptionAsync(12, updateDto); + + // Assert + result.Should().BeTrue(); + var updatedCategory = await dbContext.Categories.FindAsync(12); + updatedCategory!.Description.Should().Be("Updated description"); + } + + [Fact] + public async Task UpdateCategoryDescriptionAsync_ShouldReturnFalse_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + var categoryService = new CategoryService(dbContext); + + var updateDto = new UpdateCategoryDescriptionDto + { + Description = "Updated description" + }; + + // Act + var result = await categoryService.UpdateCategoryDescriptionAsync(13, updateDto); + + // Assert + result.Should().BeFalse(); + } + + [Fact] + public async Task DeleteCategoryAsync_ShouldDeleteCategory_WhenCategoryExists() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + dbContext.Categories.Add(new Category { CategoryID = 11, Name = "Electronics11", Description = "Electronic items" }); + await dbContext.SaveChangesAsync(); + + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.DeleteCategoryAsync(11); + + // Assert + result.Should().BeTrue(); + (await dbContext.Categories.CountAsync()).Should().Be(0); + } + + [Fact] + public async Task DeleteCategoryAsync_ShouldReturnFalse_WhenCategoryDoesNotExist() + { + // Arrange + var dbContext = GetInMemoryDbContext(); + var categoryService = new CategoryService(dbContext); + + // Act + var result = await categoryService.DeleteCategoryAsync(11); + + // Assert + result.Should().BeFalse(); + } +} diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/copoilot-sample.Test.csproj b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/copoilot-sample.Test.csproj new file mode 100644 index 0000000..edf7fa9 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/copoilot-sample.Test.csproj @@ -0,0 +1,31 @@ + + + + net8.0 + copoilot_sample.Test + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.sln b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.sln new file mode 100644 index 0000000..57968b3 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35828.75 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copoilot-sample.Api", "copoilot-sample.Api\copoilot-sample.Api.csproj", "{8E754013-64CD-402F-A9D1-10D3CFE753D3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copoilot-sample.Test", "copoilot-sample.Test\copoilot-sample.Test.csproj", "{0AE92036-1C6A-41C7-A84D-FE4E1A579351}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copoilot-sample.DataAccess", "DataAccess\copoilot-sample.DataAccess.csproj", "{8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E754013-64CD-402F-A9D1-10D3CFE753D3}.Release|Any CPU.Build.0 = Release|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0AE92036-1C6A-41C7-A84D-FE4E1A579351}.Release|Any CPU.Build.0 = Release|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B78DD698-ADCA-4195-AD2C-9DFDB1A3F59E} + EndGlobalSection +EndGlobal From 47392b15f41a08370187c8c2e94e489d03a9c38d Mon Sep 17 00:00:00 2001 From: "swagath.bairi" Date: Thu, 15 May 2025 12:07:51 -0700 Subject: [PATCH 02/66] updated copoilot help documentation on DotnetCoreApi samploe project --- .../Images/gh-cp-header.png | Bin 0 -> 41800 bytes .../Images/open-gh-cp-chat.png | Bin 0 -> 34426 bytes .../copoilot-sample.Api.csproj | 4 ++++ .../copoilot-sample.Api/copoilot.md | 20 +++++++++++++++++- 4 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/gh-cp-header.png create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/open-gh-cp-chat.png diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/gh-cp-header.png b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/gh-cp-header.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1abbfa869528a4ea8e966f7e1fc15ba70b4a63 GIT binary patch literal 41800 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 literal 0 HcmV?d00001 diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/open-gh-cp-chat.png b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/open-gh-cp-chat.png new file mode 100644 index 0000000000000000000000000000000000000000..4b5300ac39f9dd5bad8baa0c149988ffde681d05 GIT binary patch literal 34426 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 QWLTu6p#CTFu0`=l}o! literal 0 HcmV?d00001 diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj index ee8e8fb..2cbbb9e 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj @@ -19,4 +19,8 @@ + + + + diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md index cfd413f..1932fd3 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md @@ -1,6 +1,21 @@ # Copoilot chat +To open the copilot chat, click on the copilot icon in the top right corner of the Visual Studio window. +Refer : https://learn.microsoft.com/en-us/visualstudio/ide/visual-studio-github-copilot-chat?view=vs-2022 + +![Image-open-gh-cp-chat](./Images/open-gh-cp-chat.png) + +The github copilot chat window will open on the right side of the Visual Studio window. and its header has the following options: +1. Chat thread dropdown: Select a thread to view the chat history. +1. Create new thread button: Click to create a new chat thread. +1. Edit thread button: Click to edit the current thread. +1. Delete thread button: Click to delete the current thread. + + ![Copilot chat window](./Images/gh-cp-header.png) + + ## Ask mode in Copilot -1. Explain the code: +Open the copilot chat thread and ask by clicking on "Create new thread button". +1. 1. Explain the code: ``` @workspace , Explain the DataAccess project in the solution. Also explain how is it being used and triggerred ``` @@ -19,6 +34,7 @@ ``` ## Edit Mode in Copoilot +Click on Create new Edits thread button to open the edit mode chat. 1. Document the code: ``` @workspace , Document the productAttributeService class and its methods. @@ -34,6 +50,8 @@ ``` # Inline chat with copoilot +To open inline chat, select a code block and press "Alt + /". + 1. Explain about a selected code: 2. Ask copoilot to write code: From ea39485377c04cb60fc78660ed0db28316a311a4 Mon Sep 17 00:00:00 2001 From: "swagath.bairi" Date: Thu, 15 May 2025 13:08:23 -0700 Subject: [PATCH 03/66] updated documentation for DotnetCoreApi project --- .../copoilot-sample.Api/DBSetup.md | 30 ++-- .../copoilot-sample.Api/copoilot.md | 137 ++++++++++-------- 2 files changed, 91 insertions(+), 76 deletions(-) diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md index 65e8606..079abff 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md @@ -1,12 +1,14 @@ # Database Setup -1. Need a MsSql DB in localhost. download and install Developer or Express versions. Refer https://www.microsoft.com/en-us/sql-server/sql-server-downloads -2. Create a database named "Inventory" -3. Run below DDL statements to create necessary tables +1. Install MS SQL Server (Developer or Express) on localhost. + Download: +2. Create a database named **Inventory**. +3. Run the following DDL statements to create the necessary tables: -``` +```sql -- Inventory Database schema ---Categories Table + +-- Categories Table CREATE TABLE Categories ( CategoryID INT PRIMARY KEY IDENTITY(1,1), Name NVARCHAR(100) NOT NULL, @@ -15,7 +17,7 @@ CREATE TABLE Categories ( FOREIGN KEY (ParentCategoryID) REFERENCES Categories(CategoryID) ); ---Products Table +-- Products Table CREATE TABLE Products ( ProductID INT PRIMARY KEY IDENTITY(1,1), Name NVARCHAR(200) NOT NULL, @@ -29,7 +31,7 @@ CREATE TABLE Products ( FOREIGN KEY (CategoryID) REFERENCES Categories(CategoryID) ); ---Pricing Table +-- Pricing Table CREATE TABLE ProductPrices ( PriceID INT PRIMARY KEY IDENTITY(1,1), ProductID INT NOT NULL, @@ -40,7 +42,7 @@ CREATE TABLE ProductPrices ( FOREIGN KEY (ProductID) REFERENCES Products(ProductID) ); ---Inventory Table +-- Inventory Table CREATE TABLE Inventory ( InventoryID INT PRIMARY KEY IDENTITY(1,1), ProductID INT NOT NULL, @@ -49,7 +51,7 @@ CREATE TABLE Inventory ( FOREIGN KEY (ProductID) REFERENCES Products(ProductID) ); ---Product Attributes +-- Product Attributes CREATE TABLE ProductAttributes ( AttributeID INT PRIMARY KEY IDENTITY(1,1), ProductID INT NOT NULL, @@ -58,7 +60,7 @@ CREATE TABLE ProductAttributes ( FOREIGN KEY (ProductID) REFERENCES Products(ProductID) ); ---Product Reviews +-- Product Reviews CREATE TABLE ProductReviews ( ReviewID INT PRIMARY KEY IDENTITY(1,1), ProductID INT NOT NULL, @@ -68,12 +70,11 @@ CREATE TABLE ProductReviews ( ReviewDate DATETIME DEFAULT GETDATE(), FOREIGN KEY (ProductID) REFERENCES Products(ProductID) ); - ``` -4. Run below DML statements to insert some sample data +4. Run the following DML statements to insert sample data: -``` +```sql -- Insert Categories INSERT INTO Categories (Name, Description, ParentCategoryID) VALUES ('Electronics', 'Electronic gadgets and devices', NULL), @@ -128,7 +129,6 @@ INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES (4, 'Emma Stone', 4, 'Very comfortable fit and great sound.'), (4, 'John Doe', 2, 'Connection drops sometimes.'); - -- More Products INSERT INTO Products (Name, Description, SKU, CategoryID, Brand) VALUES ('ThinkMate Pro 14', 'Durable business laptop with excellent battery life', 'SKU-TMP14', 2, 'ThinkCorp'), @@ -167,6 +167,4 @@ INSERT INTO ProductReviews (ProductID, ReviewerName, Rating, Comment) VALUES (6, 'Natasha Romanoff', 4, 'Excellent camera quality and battery.'), (7, 'Steve Rogers', 5, 'Lasts all day. Perfect for travel.'), (7, 'Tony Stark', 3, 'Charges fast, but gets warm during use.'); - - ``` diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md index 1932fd3..9de6623 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md +++ b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md @@ -1,63 +1,80 @@ -# Copoilot chat -To open the copilot chat, click on the copilot icon in the top right corner of the Visual Studio window. -Refer : https://learn.microsoft.com/en-us/visualstudio/ide/visual-studio-github-copilot-chat?view=vs-2022 +# Copilot Chat + +To open the Copilot chat, click on the Copilot icon in the top right corner of the Visual Studio window. +Refer: [Visual Studio GitHub Copilot Chat Documentation](https://learn.microsoft.com/en-us/visualstudio/ide/visual-studio-github-copilot-chat?view=vs-2022) ![Image-open-gh-cp-chat](./Images/open-gh-cp-chat.png) -The github copilot chat window will open on the right side of the Visual Studio window. and its header has the following options: -1. Chat thread dropdown: Select a thread to view the chat history. -1. Create new thread button: Click to create a new chat thread. -1. Edit thread button: Click to edit the current thread. -1. Delete thread button: Click to delete the current thread. - - ![Copilot chat window](./Images/gh-cp-header.png) - - -## Ask mode in Copilot -Open the copilot chat thread and ask by clicking on "Create new thread button". -1. 1. Explain the code: - ``` - @workspace , Explain the DataAccess project in the solution. Also explain how is it being used and triggerred - ``` -2. Ask copoilot to implement a feature: - ``` - @workspace, In ProductService class, AddProductAsync method, add a check to see if the product already exists in the database. If it does, throw a custom exception named ProductAlreadyExistsException. - ``` -3. Ask copilot to implement a feature: - ``` - @workspace , Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle db operations and add it to DI. - Also add necessary Dto models. - ``` -4. Ask copoilot about an exception: - ``` - Run GetProducts end point and check if there are any exceptions, and explore "Analyze with copoilot" - ``` - -## Edit Mode in Copoilot -Click on Create new Edits thread button to open the edit mode chat. -1. Document the code: - ``` - @workspace , Document the productAttributeService class and its methods. - ``` -2. Implement a feature: - ``` - @workspace , Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle db operations and add it to DI. - Also add necessary Dto models. - ``` -3. Create unit tests for the code: - ``` - @workspace , Create unit tests for the ProductAttributeService class and its methods. - ``` - -# Inline chat with copoilot -To open inline chat, select a code block and press "Alt + /". - -1. Explain about a selected code: - -2. Ask copoilot to write code: - ``` - In ProductService class, AddProductAsync method, select the method body and click "Alt + /" to open inline chat. - Please add a check if teh Category already exists in the database. If it does, throw a exception. - ``` - - \ No newline at end of file +The GitHub Copilot chat window will open on the right side of the Visual Studio window. Its header has the following options: + +1. **Chat thread dropdown:** Select a thread to view the chat history. +2. **Create new thread button:** Click to create a new chat thread. +3. **Edit thread button:** Click to edit the current thread. +4. **Delete thread button:** Click to delete the current thread. + +![Copilot chat window](./Images/gh-cp-header.png) + +## Ask Mode in Copilot + +Open the Copilot chat thread and ask by clicking on the "Create new thread" button. + +### Examples + +1. **Explain the code:** + ```text + @workspace, Explain the DataAccess project in the solution. Also explain how it is being used and triggered. + ``` + +2. **Ask Copilot to implement a feature:** + ```text + @workspace, In ProductService class, AddProductAsync method, add a check to see if the product already exists in the database. If it does, throw a custom exception named ProductAlreadyExistsException. + ``` + +3. **Create a controller and service:** + ```text + @workspace, Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle DB operations and add it to DI. Also add necessary DTO models. + ``` + +4. **Ask Copilot about an exception:** + ```text + Run GetProducts endpoint and check if there are any exceptions, and explore "Analyze with Copilot". + ``` + +5. **Generate integration code:** + ```text + @workspace, I am trying to consume CategoryController endpoint in UI TypeScript project. It uses axios for HTTP calls, please generate TS code with interfaces. + ``` + +## Edit Mode in Copilot + +Click on the "Create new Edits thread" button to open the edit mode chat. + +### Examples + +1. **Document the code:** + ```text + @workspace, Document the ProductAttributeService class and its methods. + ``` + +2. **Implement a feature:** + ```text + @workspace, Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle DB operations and add it to DI. Also add necessary DTO models. + ``` + +3. **Create unit tests:** + ```text + @workspace, Create unit tests for the ProductAttributeService class and its methods. + ``` + +## Inline Chat with Copilot + +To open inline chat, select a code block and press `Alt + /`. + +### Examples + +1. **Explain a selected code block.** +2. **Ask Copilot to write code:** + ```text + In ProductService class, AddProductAsync method, select the method body and click "Alt + /" to open inline chat. + Please add a check if the Category already exists in the database. If it does, throw an exception. + ``` From 56e2e4e8a4cbae8dec65e3bea0112a428f54b104 Mon Sep 17 00:00:00 2001 From: Derek Huynen Date: Thu, 15 May 2025 14:01:45 -0700 Subject: [PATCH 04/66] lesson 01 --- _schedule/01-github_copilot.md | 45 --------- lessons/01-github-copilot-tools.md | 81 +++++++++++++++ lessons/Labs/01a-exploring-copilot-ask.md | 99 +++++++++++++++++++ lessons/Labs/01b-exploring-copilot-edit.md | 71 +++++++++++++ lessons/Labs/01c-exploring-copilot-agent.md | 1 + lessons/Labs/01d-exploring-copilot-inline.md | 98 ++++++++++++++++++ lessons/Labs/01e-exploring-copilot-website.md | 80 +++++++++++++++ .../_schedule}/00-copilot_setup.md | 0 lessons/_schedule/01-github_copilot.md | 36 +++++++ .../_schedule}/02-prompt_engineering.md | 0 .../_schedule}/03-design_patterns.md | 0 {_schedule => lessons/_schedule}/outline.md | 0 12 files changed, 466 insertions(+), 45 deletions(-) delete mode 100644 _schedule/01-github_copilot.md create mode 100644 lessons/01-github-copilot-tools.md create mode 100644 lessons/Labs/01a-exploring-copilot-ask.md create mode 100644 lessons/Labs/01b-exploring-copilot-edit.md create mode 100644 lessons/Labs/01c-exploring-copilot-agent.md create mode 100644 lessons/Labs/01d-exploring-copilot-inline.md create mode 100644 lessons/Labs/01e-exploring-copilot-website.md rename {_schedule => lessons/_schedule}/00-copilot_setup.md (100%) create mode 100644 lessons/_schedule/01-github_copilot.md rename {_schedule => lessons/_schedule}/02-prompt_engineering.md (100%) rename {_schedule => lessons/_schedule}/03-design_patterns.md (100%) rename {_schedule => lessons/_schedule}/outline.md (100%) diff --git a/_schedule/01-github_copilot.md b/_schedule/01-github_copilot.md deleted file mode 100644 index 68afdf6..0000000 --- a/_schedule/01-github_copilot.md +++ /dev/null @@ -1,45 +0,0 @@ -## 🚀 Workshop 1: Getting Started with GitHub Copilot - -**Duration:** 90–120 minutes - -### Objectives - -- Understand what Copilot is and how it works -- Explore all the ways you can use Copilot -- Learn to interact with Copilot through examples - -### Agenda - -#### 1. Introduction to GitHub Copilot (20 min) - -- What is Copilot? -- LLM basics (GPT-4 vs GPT-3.5) -- Where Copilot fits in your workflow - -#### 2. Overview of Copilot Tools (10 min) - -- IDE usage: Inline, panel, chat -- CLI commands and GitHub web UI - -#### 3. Hands-On: Exploring Copilot Modes (30 min) - -- Inline suggestions: writing functions, completing code -- Copilot Chat: asking for explanations or fixing bugs -- Copilot Edit: refactor an existing function -- Copilot Agent (demo): task automation - -#### 4. Example Use Cases (20 min) - -- Generate CRUD endpoints -- Add comments and docstrings -- Translate Python to JavaScript -- Use with shell scripts and Dockerfiles - -#### 5. Best Practices (10 min) - -- Use comments for guidance -- Write partial code for better completions - -#### 6. Open Q&A (10–15 min) - ---- diff --git a/lessons/01-github-copilot-tools.md b/lessons/01-github-copilot-tools.md new file mode 100644 index 0000000..815bc41 --- /dev/null +++ b/lessons/01-github-copilot-tools.md @@ -0,0 +1,81 @@ +## Lesson 01 - GitHub Copilot Tools + +Explore the different tools available within GitHub Copilot to enhance your development workflow. Below are sections highlighting each tool, including detailed descriptions, use cases, and links to dedicated labs for hands-on learning. + +## Overview + +GitHub Copilot provides developers with powerful AI-assisted coding tools designed to streamline coding, debugging, optimization, and automation tasks. These tools integrate seamlessly into various development environments, improving productivity and efficiency across multiple stages of the software development lifecycle. + +### 1. GitHub Copilot Ask + +Engage interactively with Copilot to ask questions, request code suggestions, and optimize your code directly within a conversational interface. Copilot Ask is ideal for understanding existing code, exploring implementation strategies, and generating complex code snippets through conversational prompts. + +**Example use cases:** + +- Clarifying the logic of unfamiliar or complex code. +- Requesting code examples or implementations of specific features. +- Improving existing code with optimization suggestions. + +- **Lab:** [Exploring GitHub Copilot Chat](Labs/01a-exploring-copilot-ask.md) + +### 2. GitHub Copilot Edit + +Use Copilot Edit to perform extensive refactoring and optimizations across larger codebases by providing comprehensive context-aware edits. Copilot Edit analyzes the entire file or multiple files to suggest impactful, cohesive changes, significantly enhancing code maintainability and readability. + +**Example use cases:** + +- Refactoring legacy code to adhere to modern coding standards. +- Updating methods or classes across multiple files to ensure consistency. +- Optimizing large code blocks or implementing significant logic changes. + +- **Lab:** [Exploring Github Copilot Edit](Labs/01b-exploring-copilot-edit.md) + +### 3. GitHub Copilot Agent + +Leverage Copilot Agent to automate repetitive tasks and workflows, integrating seamlessly within your development environment. This tool acts as an intelligent assistant, automating tasks such as generating boilerplate code, managing dependencies, and routine maintenance. + +**Example use cases:** + +- Automatically generating boilerplate for new projects or components. +- Managing dependency updates and routine environment configurations. +- Automating code reviews and routine coding checks. + +- **Lab:** [Automating Tasks with Copilot Agent](Labs/01c-exploring-copilot-agent.md) + +### 4. GitHub Copilot Inline + +Enhance productivity by using inline prompts directly within your editor, enabling quick code changes, method generation, and small incremental improvements without leaving the coding context. Ideal for quick, iterative coding and minor adjustments. + +**Example use cases:** + +- Quickly renaming methods or variables across a single file. +- Generating and inserting simple code blocks or functions. +- Rapid prototyping within the editor itself. + +- **Lab:** [Exploring Github Copilot Inline](Labs/01d-exploring-copilot-inline.md) + +### 5. GitHub Copilot Website + +Utilize the Copilot Web interface to explore suggestions, manage preferences, and gain insights into your coding habits and productivity. This centralized interface is helpful for reviewing usage analytics, setting global preferences, and accessing educational resources. + +**Example use cases:** + +- Reviewing coding suggestions outside of your IDE. +- Analyzing your coding patterns to identify improvement areas. +- Managing subscription details and preferences. + +- **Lab:** [Navigating Copilot's Web Interface](Labs/01e-github-copilot-website.md) + +### 6. GitHub Copilot CLI + +Streamline command-line operations by generating shell commands and automating tasks directly from your terminal. Copilot CLI is especially valuable for developers who prefer terminal-based interactions, enabling fast and efficient execution of complex shell commands. + +**Example use cases:** + +- Generating complex git commands for repository management. +- Creating automation scripts quickly through natural language prompts. +- Executing system-level operations without extensive command-line expertise. + +- **Lab:** [Mastering GitHub Copilot CLI](#) Coming Soon + +--- diff --git a/lessons/Labs/01a-exploring-copilot-ask.md b/lessons/Labs/01a-exploring-copilot-ask.md new file mode 100644 index 0000000..8947c61 --- /dev/null +++ b/lessons/Labs/01a-exploring-copilot-ask.md @@ -0,0 +1,99 @@ +### Lab: Exploring GitHub Copilot Ask + +## Overview + +**Goal:** +Learn how to use GitHub Copilot Ask to explore, understand, and improve your codebase by asking natural language questions directly within Visual Studio Code. + +**Estimated Duration:** +15-20 minutes + +**Audience:** +Developers, QA testers, DevOps engineers, and Technical Writers. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Chat (requires a Copilot subscription) +- Familiarity with a sample project (C#, JavaScript, or Python) + +## Lab Steps + +### 1. Launch Copilot Chat + +- Open Visual Studio Code. +- Navigate to the Copilot Chat icon in the activity bar or use the shortcut `Ctrl + Alt + I` (Windows/Linux) or `Cmd + Option + I` (Mac). +- Ensure the model is set to **GPT-4o** in the settings for best results. + +### 2. Ask Contextual Questions + +- Browse to `src/Ordering.API/Apis` folder and open the `OrdersApi.cs` file. +- In the Copilot Chat prompt, type: + +```text +how does this service work? +``` + +Observe the GitHub Copilot response explaining the code. + +Alternatively, you can use a command like: + +```text +/explain +``` + +Continue the conversation in the same context by asking follow-up questions: + +```text +how can I optimize logging in this code? +``` + +Observe and review the suggested improvements. + +(Optional) + +- You can copy the suggested code manually into your file. +- Or click the icons above the suggestion pane to: + - Insert at Cursor + - Apply in Editor + - Copy to Clipboard +- (Optional) Save the file to retain changes. + +### 3. Ask for New Code + +Type the following prompt in Copilot Chat: + +```text +Generate a method that calculates the total with tax and applies a discount. +``` + +Observe how Copilot suggests a complete function and copy or insert it into your editor. + +### 4. Ask for Documentation + +Try a prompt such as: + +```text +Write documentation comments for this class. +``` + +Accept and format the response into your codebase. + +## Best Practices + +- **Be Specific:** Include relevant class, method, or file names when asking questions. +- **Break Down Requests:** Use step-by-step prompts if Copilot struggles with more complex tasks. +- **Use Follow-ups:** Build on previous answers to refine results. +- **Insert Carefully:** Always review suggestions before inserting into production code. +- **Try / Commands:** Use `/explain`, `/generate`, `/tests`, etc., to streamline specific actions. + +## Summary + +By completing this lab, you’ve learned to: + +- Ask Copilot natural language questions about your code. +- Generate new code using descriptive prompts. +- Request documentation or refactoring suggestions. + +These techniques will help you incorporate GitHub Copilot Chat into your daily development workflow effectively. diff --git a/lessons/Labs/01b-exploring-copilot-edit.md b/lessons/Labs/01b-exploring-copilot-edit.md new file mode 100644 index 0000000..e40c808 --- /dev/null +++ b/lessons/Labs/01b-exploring-copilot-edit.md @@ -0,0 +1,71 @@ +### Lab: Exploring GitHub Copilot Edit + +## Overview + +**Goal:** +Use GitHub Copilot Edit to refactor, enhance, and optimize code with large-scope changes using natural language prompts across multiple files or code blocks. + +**Estimated Duration:** +15-20 minutes + +**Audience:** +Developers, QA testers, DevOps engineers, and Technical Writers working with medium to large codebases. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Edit (in Preview as part of GitHub Copilot Chat) +- Familiarity with a sample project (C# preferred for this example) + +## Lab Steps + +### 1. Reset Your Code + +- Undo or discard any previous changes made to the `Order` class (if following from a previous lab). +- Ensure you have a clean version of `Order.cs` open for editing. + +### 2. Launch Copilot Edit + +- Open the Copilot Chat interface by clicking the Copilot icon in the bottom-right corner of Visual Studio Code. +- In the Copilot Chat window, click the **Copilot Edits** tab in the top-left corner. + +![copilot edit](./images/copilot-edits.png) + +### 3. Add the File to Context + +- Click `+ Add Files` and select the `Order.cs` file from your project to add it to the edit context. + +### 4. Issue a Refactoring Prompt + +In the Copilot Edit prompt, enter the following natural language instruction: + +```plaintext +Refactor the GetTotal method to include taxes and discounts. Get the taxes from a new public property named Taxes and calculate the discounts as the sum of the Discount property for each OrderItem in the order. +``` + +Copilot Edit will now scan the full file and apply necessary updates across the class. + +Review the proposed changes directly in the chat interface. + +### 5. Apply the Edits + +- Click **Apply in Editor** to confirm the changes and commit them to your working file. +- Test and verify the updated functionality. + +## Best Practices + +- **Edit in Context:** Use Copilot Edit for large-scale or multi-line changes where standard inline prompts are insufficient. +- **Preview Before Apply:** Always review the full diff before applying edits. +- **Use Descriptive Prompts:** Give specific names, logic, or intended outcomes in your prompt. +- **Iterate Prompting:** If the result is not ideal, revise your prompt and re-run the edit. + +## Summary + +By completing this lab, you’ve learned to: + +- Use Copilot Edit to modify an entire class file based on a natural language instruction. +- Apply and verify complex changes like tax and discount logic refactoring. +- Leverage full-file editing to streamline development and maintenance in real-world scenarios. + +This lab demonstrates how GitHub Copilot Edit can boost productivity for developers managing and evolving mid-to-large sized codebases. diff --git a/lessons/Labs/01c-exploring-copilot-agent.md b/lessons/Labs/01c-exploring-copilot-agent.md new file mode 100644 index 0000000..e222323 --- /dev/null +++ b/lessons/Labs/01c-exploring-copilot-agent.md @@ -0,0 +1 @@ +//TODO: UPDATE diff --git a/lessons/Labs/01d-exploring-copilot-inline.md b/lessons/Labs/01d-exploring-copilot-inline.md new file mode 100644 index 0000000..8b518a5 --- /dev/null +++ b/lessons/Labs/01d-exploring-copilot-inline.md @@ -0,0 +1,98 @@ +### Lab: Exploring GitHub Copilot Edit + +## Overview + +**Goal:** +Learn how to use GitHub Copilot's inline chat and autocomplete features to make quick, incremental changes directly within the context of your code editor. You’ll practice renaming, generating, and editing methods using inline prompts. + +**Estimated Duration:** +15-20 minutes + +**Audience:** +Developers looking to boost their productivity through fast, context-aware code editing within their IDE. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot (with inline chat feature) +- Familiarity with C# or a similar object-oriented language + +## Lab Steps + +### 1. Open Target File + +- Navigate to the `samples/eshop/src/Ordering.Domain/AggregatesModel/OrderAggregate` folder in Visual Studio Code. +- Open the `Order.cs` file for editing. + +### 2. Rename the Method + +- Locate the existing `GetTotal` public method at the bottom of the `Order` class. +- Place your cursor on `GetTotal`. +- Press `Ctrl+I` (or use right-click > Copilot: Ask) to open an inline Copilot prompt. +- Enter the following prompt: + + ```plaintext + Rename symbol `GetTotal` to `GetSubTotal` + ``` + + Press Enter. Review the rename suggestions and apply them. + +### 3. Add a New Total Method + +- With the same file still open, open a new inline Copilot prompt. +- Enter the following prompt: + + ```plaintext + Add a new public member named GetTotal that is calculated as sum of order items and a new public property of type decimal named taxes and subtracts the sum of items in a public property of type List named Discounts + ``` + + Accept the generated code suggestion. + +### 4. Create a Discount Helper Method + +- In a new inline prompt, enter: + + ```plaintext + Add an empty public member named GetOrderDiscounts that returns a decimal + ``` + + - Accept the method stub and close the prompt. + - Delete the return statement. + - Observe the "ghost text" Copilot suggests in gray text. + - Press `Tab` to accept the suggestion, or `Ctrl + Arrow` keys to accept piece-by-piece. + +### 5. Update Discount Logic + +- Open a new inline Copilot prompt (`Ctrl+I`) inside the `GetOrderDiscounts` method. +- Enter the following: + + ```plaintext + Update `GetOrderDiscounts` method to use the `Discount` property in OrderItem + ``` + + Accept the Copilot suggestion to calculate the total discounts from all order items. + +### 6. Integrate Discount Logic + +- Return to the `GetTotal` method. +- Delete the value assigned to the `totalDiscounts` variable. +- Observe Copilot’s ghost text suggesting a call to `GetOrderDiscounts()`. +- Press `Tab` to accept the suggestion and complete the integration. + +## Best Practices + +- **Use Inline for Small Tasks:** Inline prompts are perfect for renaming, simple refactoring, or method generation. +- **Combine with Ghost Text:** Let Copilot suggest code as you type for even faster iteration. +- **Be Specific:** Clear, concise prompts yield better suggestions. +- **Stay Contextual:** Inline prompts rely on the local context, so place your cursor near relevant code. + +## Summary + +By completing this lab, you’ve learned to: + +- Use GitHub Copilot's inline chat to rename and generate methods. +- Accept ghost text to streamline minor edits and logic insertions. +- Chain inline edits for a more interactive and focused development experience. + +GitHub Copilot Inline is a fast, intuitive way to stay in the flow while writing or improving code directly in your editor. diff --git a/lessons/Labs/01e-exploring-copilot-website.md b/lessons/Labs/01e-exploring-copilot-website.md new file mode 100644 index 0000000..866d561 --- /dev/null +++ b/lessons/Labs/01e-exploring-copilot-website.md @@ -0,0 +1,80 @@ +### Lab: Exploring Github Copilot on GitHub.com + +## Overview + +**Goal:** +Learn how to interact with GitHub Copilot directly on the GitHub.com website to ask questions about code, understand repositories, and receive helpful suggestions without needing an IDE. + +**Estimated Duration:** +15-20 minutes + +**Audience:** +Developers, technical writers, and students looking to explore GitHub Copilot through the web experience without setting up a local development environment. + +**Prerequisites:** + +- A GitHub account with an active GitHub Copilot subscription +- A public or private repository with some source code (JavaScript, Python, C#, etc.) +- A modern web browser + +## Lab Steps + +### 1. Navigate to a Repository + +- Open your browser and go to [https://github.com](https://github.com). +- Open a repository that contains some code (e.g., a JavaScript or Python project). +- Make sure you are signed in to your GitHub account with Copilot enabled. + +### 2. Open Copilot Chat on GitHub.com + +- Click the **Copilot Chat** button in the top-right corner of the repository page (only visible to users with an active Copilot subscription). +- This will open a side panel where you can interact with Copilot. + +### 3. Ask a More Interesting Question + +- In the Copilot Chat panel, type the following question: + + ```text + How would I convert this app to use a serverless architecture with AWS Lambda? + ``` + + Press Enter. + + Copilot will provide a conceptual answer based on the code it sees in the repository, such as identifying entry points, explaining how to refactor handlers, and suggesting deployment strategies. + +### 4. Ask About Specific Files + +- Open a specific file in the repository (e.g., `main.py`, `app.js`, or `OrderService.cs`). +- With the file open, go back to the Copilot Chat panel and ask: + + ```text + Explain what this file does. + ``` + + - Observe how Copilot provides a contextual explanation. + +### 5. Ask for Improvements + +- Now try asking: + + ```text + What improvements can I make to this file's error handling? + ``` + + - Review the suggestions and consider how they could apply to your code. + +## Best Practices + +- **Be Specific:** Reference files or lines for more precise answers. +- **Ask Iteratively:** Break your questions down if Copilot gives vague results. +- **Explore Freely:** Ask about repo structure, functions, or how to refactor code. + +## Summary + +By completing this lab, you’ve learned to: + +- Access and use GitHub Copilot directly on GitHub.com +- Ask questions about a repository or individual files +- Get actionable feedback and code suggestions without needing to open an IDE + +GitHub Copilot on the web is perfect for quick insights, documentation reviews, and exploring unfamiliar codebases in the browser. diff --git a/_schedule/00-copilot_setup.md b/lessons/_schedule/00-copilot_setup.md similarity index 100% rename from _schedule/00-copilot_setup.md rename to lessons/_schedule/00-copilot_setup.md diff --git a/lessons/_schedule/01-github_copilot.md b/lessons/_schedule/01-github_copilot.md new file mode 100644 index 0000000..554a96e --- /dev/null +++ b/lessons/_schedule/01-github_copilot.md @@ -0,0 +1,36 @@ +## 🚀 Workshop 1: Getting Started with GitHub Copilot + +**Duration:** 90–120 minutes + +### Objectives + +- Understand what Copilot is and how it works +- Explore all the ways you can use Copilot +- Learn to interact with Copilot through examples + +### Agenda + +#### 1. Introduction to GitHub Copilot (20 min) (Power Point) + +- What is Copilot? +- LLM basics (GPT-4 vs GPT-3.5) +- Where Copilot fits in your workflow + +#### 2. Overview of Copilot Tools (10-15 min) + +- [Lesson 01: Copilot Tools Overview](../01-github-copilot-tools.md) +- IDE usage: inline, chat, edit, agent +- CLI commands and GitHub web UI + +#### 3. Hands-On Labs for Each Tool (90 min) + +- **Copilot Chat:** [Exploring GitHub Copilot Chat](../Labs/01a-exploring-copilot-ask.md) +- **Copilot Edit:** [Exploring GitHub Copilot Edit](../Labs/01b-exploring-copilot-edit.md) +- **Copilot Agent:** [Automating Tasks with Copilot Agent](../Labs/01c-exploring-copilot-agent.md) +- **Copilot Inline:** [Exploring GitHub Copilot Inline](../Labs/01d-exploring-copilot-inline.md) +- **Copilot Website:** [Exploring GitHub Copilot on GitHub.com](../Labs/01e-exploring-copilot-website.md) +- **Copilot CLI:** Coming soon... + +#### 5. Open Q&A (10–15 min) + +# Any questions or clarifications about the labs or Copilot in general. diff --git a/_schedule/02-prompt_engineering.md b/lessons/_schedule/02-prompt_engineering.md similarity index 100% rename from _schedule/02-prompt_engineering.md rename to lessons/_schedule/02-prompt_engineering.md diff --git a/_schedule/03-design_patterns.md b/lessons/_schedule/03-design_patterns.md similarity index 100% rename from _schedule/03-design_patterns.md rename to lessons/_schedule/03-design_patterns.md diff --git a/_schedule/outline.md b/lessons/_schedule/outline.md similarity index 100% rename from _schedule/outline.md rename to lessons/_schedule/outline.md From bebf07467e0fdf599a50d5b36083a16d2ce6f072 Mon Sep 17 00:00:00 2001 From: Derek Huynen Date: Thu, 15 May 2025 14:07:55 -0700 Subject: [PATCH 05/66] moving into 01 folder --- .../01-github-copilot-tools.md | 10 +++++----- .../labs}/01a-exploring-copilot-ask.md | 0 .../labs}/01b-exploring-copilot-edit.md | 0 .../labs}/01c-exploring-copilot-agent.md | 0 .../labs}/01d-exploring-copilot-inline.md | 0 .../labs}/01e-exploring-copilot-website.md | 0 lessons/_schedule/01-github_copilot.md | 12 ++++++------ 7 files changed, 11 insertions(+), 11 deletions(-) rename lessons/{ => 01-github-copilot-tools}/01-github-copilot-tools.md (92%) rename lessons/{Labs => 01-github-copilot-tools/labs}/01a-exploring-copilot-ask.md (100%) rename lessons/{Labs => 01-github-copilot-tools/labs}/01b-exploring-copilot-edit.md (100%) rename lessons/{Labs => 01-github-copilot-tools/labs}/01c-exploring-copilot-agent.md (100%) rename lessons/{Labs => 01-github-copilot-tools/labs}/01d-exploring-copilot-inline.md (100%) rename lessons/{Labs => 01-github-copilot-tools/labs}/01e-exploring-copilot-website.md (100%) diff --git a/lessons/01-github-copilot-tools.md b/lessons/01-github-copilot-tools/01-github-copilot-tools.md similarity index 92% rename from lessons/01-github-copilot-tools.md rename to lessons/01-github-copilot-tools/01-github-copilot-tools.md index 815bc41..24efb34 100644 --- a/lessons/01-github-copilot-tools.md +++ b/lessons/01-github-copilot-tools/01-github-copilot-tools.md @@ -16,7 +16,7 @@ Engage interactively with Copilot to ask questions, request code suggestions, an - Requesting code examples or implementations of specific features. - Improving existing code with optimization suggestions. -- **Lab:** [Exploring GitHub Copilot Chat](Labs/01a-exploring-copilot-ask.md) +- **Lab:** [Exploring GitHub Copilot Chat](labs/01a-exploring-copilot-ask.md) ### 2. GitHub Copilot Edit @@ -28,7 +28,7 @@ Use Copilot Edit to perform extensive refactoring and optimizations across large - Updating methods or classes across multiple files to ensure consistency. - Optimizing large code blocks or implementing significant logic changes. -- **Lab:** [Exploring Github Copilot Edit](Labs/01b-exploring-copilot-edit.md) +- **Lab:** [Exploring Github Copilot Edit](labs/01b-exploring-copilot-edit.md) ### 3. GitHub Copilot Agent @@ -40,7 +40,7 @@ Leverage Copilot Agent to automate repetitive tasks and workflows, integrating s - Managing dependency updates and routine environment configurations. - Automating code reviews and routine coding checks. -- **Lab:** [Automating Tasks with Copilot Agent](Labs/01c-exploring-copilot-agent.md) +- **Lab:** [Automating Tasks with Copilot Agent](labs/01c-exploring-copilot-agent.md) ### 4. GitHub Copilot Inline @@ -52,7 +52,7 @@ Enhance productivity by using inline prompts directly within your editor, enabli - Generating and inserting simple code blocks or functions. - Rapid prototyping within the editor itself. -- **Lab:** [Exploring Github Copilot Inline](Labs/01d-exploring-copilot-inline.md) +- **Lab:** [Exploring Github Copilot Inline](labs/01d-exploring-copilot-inline.md) ### 5. GitHub Copilot Website @@ -64,7 +64,7 @@ Utilize the Copilot Web interface to explore suggestions, manage preferences, an - Analyzing your coding patterns to identify improvement areas. - Managing subscription details and preferences. -- **Lab:** [Navigating Copilot's Web Interface](Labs/01e-github-copilot-website.md) +- **Lab:** [Navigating Copilot's Web Interface](labs/01e-github-copilot-website.md) ### 6. GitHub Copilot CLI diff --git a/lessons/Labs/01a-exploring-copilot-ask.md b/lessons/01-github-copilot-tools/labs/01a-exploring-copilot-ask.md similarity index 100% rename from lessons/Labs/01a-exploring-copilot-ask.md rename to lessons/01-github-copilot-tools/labs/01a-exploring-copilot-ask.md diff --git a/lessons/Labs/01b-exploring-copilot-edit.md b/lessons/01-github-copilot-tools/labs/01b-exploring-copilot-edit.md similarity index 100% rename from lessons/Labs/01b-exploring-copilot-edit.md rename to lessons/01-github-copilot-tools/labs/01b-exploring-copilot-edit.md diff --git a/lessons/Labs/01c-exploring-copilot-agent.md b/lessons/01-github-copilot-tools/labs/01c-exploring-copilot-agent.md similarity index 100% rename from lessons/Labs/01c-exploring-copilot-agent.md rename to lessons/01-github-copilot-tools/labs/01c-exploring-copilot-agent.md diff --git a/lessons/Labs/01d-exploring-copilot-inline.md b/lessons/01-github-copilot-tools/labs/01d-exploring-copilot-inline.md similarity index 100% rename from lessons/Labs/01d-exploring-copilot-inline.md rename to lessons/01-github-copilot-tools/labs/01d-exploring-copilot-inline.md diff --git a/lessons/Labs/01e-exploring-copilot-website.md b/lessons/01-github-copilot-tools/labs/01e-exploring-copilot-website.md similarity index 100% rename from lessons/Labs/01e-exploring-copilot-website.md rename to lessons/01-github-copilot-tools/labs/01e-exploring-copilot-website.md diff --git a/lessons/_schedule/01-github_copilot.md b/lessons/_schedule/01-github_copilot.md index 554a96e..f3cafce 100644 --- a/lessons/_schedule/01-github_copilot.md +++ b/lessons/_schedule/01-github_copilot.md @@ -18,17 +18,17 @@ #### 2. Overview of Copilot Tools (10-15 min) -- [Lesson 01: Copilot Tools Overview](../01-github-copilot-tools.md) +- [Lesson 01: Copilot Tools Overview](../01-github-copilot-tools/01-github-copilot-tools.md) - IDE usage: inline, chat, edit, agent - CLI commands and GitHub web UI #### 3. Hands-On Labs for Each Tool (90 min) -- **Copilot Chat:** [Exploring GitHub Copilot Chat](../Labs/01a-exploring-copilot-ask.md) -- **Copilot Edit:** [Exploring GitHub Copilot Edit](../Labs/01b-exploring-copilot-edit.md) -- **Copilot Agent:** [Automating Tasks with Copilot Agent](../Labs/01c-exploring-copilot-agent.md) -- **Copilot Inline:** [Exploring GitHub Copilot Inline](../Labs/01d-exploring-copilot-inline.md) -- **Copilot Website:** [Exploring GitHub Copilot on GitHub.com](../Labs/01e-exploring-copilot-website.md) +- **Copilot Chat:** [Exploring GitHub Copilot Chat](../01-github-copilot-tools/labs/01a-exploring-copilot-ask.md) +- **Copilot Edit:** [Exploring GitHub Copilot Edit](../01-github-copilot-tools/labs/01b-exploring-copilot-edit.md) +- **Copilot Agent:** [Automating Tasks with Copilot Agent](../01-github-copilot-tools/labs/01c-exploring-copilot-agent.md) +- **Copilot Inline:** [Exploring GitHub Copilot Inline](../01-github-copilot-tools/labs/01d-exploring-copilot-inline.md) +- **Copilot Website:** [Exploring GitHub Copilot on GitHub.com](../01-github-copilot-tools/labs/01e-exploring-copilot-website.md) - **Copilot CLI:** Coming soon... #### 5. Open Q&A (10–15 min) From 15876d67a70a31f2a6390ded47cda09ea5cf9850 Mon Sep 17 00:00:00 2001 From: Derek Huynen Date: Mon, 19 May 2025 21:18:11 -0700 Subject: [PATCH 06/66] 02 --- .../01-github-copilot-tools.md | 86 +++++- .../02-prompt-engineering.md | 254 ++++++++++++++++++ lessons/README.md | 69 +++++ 3 files changed, 397 insertions(+), 12 deletions(-) create mode 100644 lessons/02-prompt-engineering/02-prompt-engineering.md create mode 100644 lessons/README.md diff --git a/lessons/01-github-copilot-tools/01-github-copilot-tools.md b/lessons/01-github-copilot-tools/01-github-copilot-tools.md index 24efb34..3ecb486 100644 --- a/lessons/01-github-copilot-tools/01-github-copilot-tools.md +++ b/lessons/01-github-copilot-tools/01-github-copilot-tools.md @@ -44,37 +44,99 @@ Leverage Copilot Agent to automate repetitive tasks and workflows, integrating s ### 4. GitHub Copilot Inline -Enhance productivity by using inline prompts directly within your editor, enabling quick code changes, method generation, and small incremental improvements without leaving the coding context. Ideal for quick, iterative coding and minor adjustments. +Enhance productivity by using inline prompts directly within your editor, enabling quick code changes, method generation, and small incremental improvements without leaving the coding context. This approach is ideal for quick, iterative coding and minor adjustments that don't require extensive context switching. + +**When to use:** + +- When making focused changes to a specific section of code +- During active development sessions where flow state is important +- For quick prototyping or iterative improvements to existing code +- When you need immediate suggestions without breaking your concentration **Example use cases:** -- Quickly renaming methods or variables across a single file. -- Generating and inserting simple code blocks or functions. -- Rapid prototyping within the editor itself. +- Quickly renaming methods or variables across a single file +- Generating and inserting simple code blocks or functions +- Implementing standard design patterns (e.g., Factory, Singleton, Observer) +- Writing unit tests for existing methods +- Converting between code formats (e.g., converting a for loop to LINQ in C#) +- Generating regular expressions for specific validation requirements +- Implementing interface methods or abstract class implementations +- Creating data models or DTOs based on existing patterns in your codebase + +**Azure-specific examples:** + +- Quickly adding Azure SDK authentication code snippets directly in your editor +- Generating Azure Storage or Cosmos DB access code inline where needed +- Converting local configuration to Azure App Configuration or Key Vault references +- Adding Azure Monitor Application Insights telemetry to existing methods - **Lab:** [Exploring Github Copilot Inline](labs/01d-exploring-copilot-inline.md) ### 5. GitHub Copilot Website -Utilize the Copilot Web interface to explore suggestions, manage preferences, and gain insights into your coding habits and productivity. This centralized interface is helpful for reviewing usage analytics, setting global preferences, and accessing educational resources. +Utilize the Copilot Web interface to explore suggestions, manage preferences, and gain insights into your coding habits and productivity. This centralized interface provides a comprehensive dashboard for reviewing usage analytics, setting global preferences, accessing educational resources, and managing your Copilot subscription. + +**When to use:** + +- For managing subscription details and billing information +- To review personal usage metrics and productivity insights +- When setting up global preferences that apply across all development environments +- To access educational materials and latest updates about Copilot capabilities +- For providing feedback on Copilot's performance and suggestions **Example use cases:** -- Reviewing coding suggestions outside of your IDE. -- Analyzing your coding patterns to identify improvement areas. -- Managing subscription details and preferences. +- Reviewing coding suggestions outside of your IDE +- Analyzing your coding patterns to identify improvement areas +- Managing subscription details and preferences +- Customizing Copilot's behavior across different programming languages +- Setting up global ignore patterns for sensitive or proprietary code +- Accessing learning resources and official documentation +- Reviewing historical usage statistics to optimize your development workflow +- Providing feedback to the GitHub team on Copilot's suggestions + +**Azure-specific examples:** + +- Accessing tailored Azure best practices resources through your Copilot dashboard +- Reviewing Azure-related code quality metrics in your Copilot analytics +- Managing Azure-specific Copilot custom instructions through the web interface +- Exploring additional Azure development resources linked from the Copilot dashboard - **Lab:** [Navigating Copilot's Web Interface](labs/01e-github-copilot-website.md) ### 6. GitHub Copilot CLI -Streamline command-line operations by generating shell commands and automating tasks directly from your terminal. Copilot CLI is especially valuable for developers who prefer terminal-based interactions, enabling fast and efficient execution of complex shell commands. +Streamline command-line operations by generating shell commands and automating tasks directly from your terminal. Copilot CLI transforms natural language descriptions into powerful command-line instructions, making complex operations accessible without requiring memorization of syntax or extensive documentation lookups. + +**When to use:** + +- For executing complex or infrequently used commands +- When working in terminal-heavy workflows +- For automating repetitive command-line tasks +- When you need to construct complex pipelines or data transformations +- If you're unfamiliar with command syntax for specific tools or platforms **Example use cases:** -- Generating complex git commands for repository management. -- Creating automation scripts quickly through natural language prompts. -- Executing system-level operations without extensive command-line expertise. +- Generating complex git commands for repository management (e.g., interactive rebasing, complex merges) +- Creating automation scripts quickly through natural language prompts +- Executing system-level operations without extensive command-line expertise +- Constructing advanced grep, sed, or awk commands for text processing +- Building deployment pipelines and CI/CD scripts +- Generating database queries or migration scripts +- Creating Docker and Kubernetes management commands +- Formulating complex data transformation pipelines using tools like jq or yq + +**Azure-specific examples:** + +- Generating Azure CLI (az) commands for resource management +- Creating complex Azure Resource Manager (ARM) deployment commands +- Building Azure DevOps pipeline definitions and commands +- Automating Azure resource monitoring and maintenance tasks +- Generating secure connection strings and authentication commands +- Creating Azure Kubernetes Service (AKS) management commands +- Developing multi-stage deployment scripts for Azure environments - **Lab:** [Mastering GitHub Copilot CLI](#) Coming Soon diff --git a/lessons/02-prompt-engineering/02-prompt-engineering.md b/lessons/02-prompt-engineering/02-prompt-engineering.md new file mode 100644 index 0000000..bd23ea3 --- /dev/null +++ b/lessons/02-prompt-engineering/02-prompt-engineering.md @@ -0,0 +1,254 @@ +## Lesson 02 - Prompt Engineering with GitHub Copilot + +Learn effective prompt engineering techniques to maximize your productivity with GitHub Copilot. This lesson covers essential concepts, best practices, and strategies to communicate effectively with AI coding assistants. + +## Overview + +Prompt engineering is the art and science of crafting effective instructions for AI systems like GitHub Copilot. By understanding how to structure your requests, you can significantly improve the quality, relevance, and accuracy of AI-generated code and responses. + +### 1. Understanding Tokens and Context Windows + +Explore how Copilot processes text through tokens and how context window limitations affect interactions. Understanding these fundamental concepts helps you optimize your prompts for better results. + +**Key concepts:** + +- What tokens are and how AI models process them +- Context window limitations and their impact on responses +- Strategies for working within token constraints +- How to prioritize information within limited context + +**When to focus on tokens and context:** + +- When working with large codebases or complex projects +- When you need to provide extensive background information +- When Copilot's responses seem incomplete or miss important context +- When you're experiencing truncated or irrelevant responses + +**Tips and tricks:** + +- Start with the most relevant information first since models pay more attention to the beginning and end of prompts +- Use clear section headers to help Copilot organize information (`### Background`, `### Requirements`, etc.) +- For large projects, focus on sharing only the most relevant files or code snippets +- When faced with token limitations, break down complex requests into sequential, smaller prompts +- Remove unnecessary comments, documentation, or boilerplate when providing code as context + +- **Lab:** [Working with Tokens and Context Windows](#) Coming Soon + +### 2. Crafting Effective Prompts + +Learn the elements that make a good prompt and how to structure your requests to get optimal responses from Copilot. Well-crafted prompts lead to more accurate, relevant, and useful code suggestions. + +**Key techniques:** + +- Being specific and clear in your instructions +- Including relevant details while avoiding unnecessary information +- Using appropriate technical terminology +- Structuring multi-part requests effectively + +**When to refine your prompting approach:** + +- When generating complex algorithms or patterns +- When implementing specific design patterns or architectural styles +- When Copilot generates code that's close but not exactly what you need +- When you need code that follows particular conventions or standards + +**Tips and tricks:** + +- Start with a clear task description: "Create a function that..." +- Specify the programming language explicitly: "In TypeScript, write a..." +- Include expected inputs and outputs: "The function should accept an array of strings and return a filtered array" +- Mention error handling requirements: "Include proper error handling for invalid inputs" +- Reference coding standards when applicable: "Follow the Angular style guide for component structure" +- Use formatting to emphasize important aspects of your request (bullet points, numbering) +- For complex requests, provide an example of similar code or pseudocode + +- **Lab:** [Crafting Effective Prompts for Code Generation](#) Coming Soon + +### 3. Adding Context in VS Code + +Master techniques for providing additional context to Copilot within VS Code, including file references and specialized annotations. These approaches help Copilot understand your project structure and requirements better. + +**Methods explored:** + +- Using the `@` symbol to reference files and directories +- Importing relevant code snippets as context +- Working with multiple files simultaneously +- Leveraging workspace information effectively + +**When to provide additional context:** + +- When implementing features that interact with existing code +- When maintaining consistency across a codebase +- When Copilot needs to understand project-specific patterns or conventions +- When working on complex systems with interdependencies + +**Tips and tricks:** + +- Use `@filename` syntax to reference specific files in your workspace +- Create a temporary file with key context from multiple files when needed +- When referencing a file, specify which parts are most relevant: "Focus on the authentication logic in @auth.service.ts" +- For complex projects, create a short description of the architecture and reference it +- When implementing interfaces or extending classes, always provide the original definitions +- Use tabs in VS Code to keep related files open, improving Copilot's awareness of context + +- **Lab:** [Providing Context in VS Code](#) Coming Soon + +### 4. Prompt Engineering Best Practices + +Discover industry-proven best practices for prompt engineering that consistently yield superior results across different coding scenarios and tasks. + +**Best practices include:** + +- Iterative refinement of prompts +- Providing examples when appropriate +- Using clear formatting and structure +- Setting explicit constraints and requirements + +**When to apply these best practices:** + +- When working on mission-critical features +- When code quality and accuracy are paramount +- When you need to ensure consistent coding style +- When implementing complex business logic or algorithms + +**Tips and tricks:** + +- Start general, then get specific: begin with a broad request, then refine based on initial responses +- Use the "few-shot" approach: provide 2-3 examples of similar code to establish patterns +- Explicitly state constraints: "The solution must be O(n) time complexity" +- For complex tasks, break down the problem into steps and prompt for each step +- Provide feedback to Copilot about what worked and didn't work in previous responses +- Use consistent terminology within your prompts and across your project +- Explicitly mention testing requirements: "Include unit tests for edge cases" + +- **Lab:** [Implementing Prompt Engineering Best Practices](#) Coming Soon + +### 5. Advanced Prompting Techniques + +Explore sophisticated prompting patterns and techniques that leverage Copilot's capabilities for complex coding tasks and specialized scenarios. + +**Advanced techniques:** + +- Chain-of-thought prompting for complex problems +- Role-based prompting for specialized code generation +- Using formal specifications in prompts +- Breaking down complex tasks into manageable prompts + +**When to use advanced techniques:** + +- When implementing complex algorithms or design patterns +- When generating specialized code (security, performance-critical) +- When standard prompting approaches yield suboptimal results +- When working on novel or unique programming challenges + +**Tips and tricks:** + +- Use chain-of-thought prompting: "Let's think through this step by step..." to guide Copilot's reasoning +- Assign roles to Copilot: "As a security expert, review this authentication code..." +- For complex systems, create a mental model: "This system follows the CQRS pattern with these components..." +- Use specification-driven prompting: provide formal requirements and acceptance criteria +- Implement conversational programming: build on previous interactions to refine solutions +- For debugging, use hypothesis testing: "Could the issue be related to X? If so, how would we fix it?" +- Use comparative prompting: "Generate two solutions to this problem using different approaches" + +- **Lab:** [Advanced Prompting Techniques](#) Coming Soon + +### 6. Prompting with GitHub Copilot Agent + +Master specialized prompting strategies for GitHub Copilot Agent to automate tasks, manage workflows, and enhance productivity through conversational interactions. + +**Agent-specific techniques:** + +- Crafting prompts for task automation +- Guiding multi-step processes effectively +- Providing feedback to refine Agent responses +- Managing complex development workflows + +**When to leverage Copilot Agent:** + +- When automating repetitive development tasks +- When working on large-scale refactoring projects +- When navigating and understanding unfamiliar codebases +- When implementing complex, multi-file features + +**Tips and tricks:** + +- Provide clear overall objectives: "Help me implement a new authentication system using JWT" +- Set expectations for the interaction: "Let's work on this feature step by step" +- Reference specific tools that Agent can use: "Use the terminal to install the required packages" +- For complex tasks, create a plan first: "Let's outline the steps before implementation" +- Guide the Agent with specific follow-up questions when responses aren't ideal +- Use the Agent to explore alternatives: "Can you suggest three different approaches to this problem?" +- Combine Agent with other Copilot tools: "Use Copilot Edit to implement the changes we discussed" +- Establish clear scope boundaries: "Focus only on the backend API for now, we'll address the frontend later" + +- **Lab:** [Effective Prompting with GitHub Copilot Agent](#) Coming Soon + +--- + +## Practical Applications + +The following examples demonstrate how effective prompt engineering can significantly improve your development workflow across various common scenarios: + +### Example 1: Generating a Complex Data Structure + +**Basic Prompt:** + +``` +Create a user class +``` + +**Improved Prompt:** + +``` +In TypeScript, create a User class with the following requirements: +- Properties for id (UUID), name, email, role (enum: admin, user, guest), and lastLogin (Date) +- Constructor that validates email format +- Method to check if user has admin privileges +- Static factory method to create a guest user +- Follow the singleton pattern for an admin user +Include appropriate TypeScript typing and JSDoc comments. +``` + +### Example 2: Implementing an Algorithm + +**Basic Prompt:** + +``` +Write code to find duplicates +``` + +**Improved Prompt:** + +``` +In Python, implement an efficient algorithm to find all duplicates in an array of integers where each integer is between 1 and n (array length). +The algorithm should: +- Run in O(n) time +- Use O(1) extra space +- Not modify the original array +Include comments explaining the approach and handle edge cases like empty arrays. +``` + +### Example 3: Using Copilot Agent for Project Setup + +**Basic Prompt:** + +``` +Help me set up a React project +``` + +**Improved Prompt:** + +``` +I need to set up a new React project with the following requirements: +1. Use Vite as the build tool +2. Configure for TypeScript support +3. Add React Router for navigation +4. Set up ESLint with Airbnb style guide +5. Configure Jest and React Testing Library for testing +6. Add TailwindCSS for styling + +Guide me through this setup step by step, explaining each command and configuration file. After setup is complete, help me create a basic project structure with components, pages, and services folders. +``` + +--- diff --git a/lessons/README.md b/lessons/README.md new file mode 100644 index 0000000..38d6b04 --- /dev/null +++ b/lessons/README.md @@ -0,0 +1,69 @@ +# GitHub Copilot Training Workshop + +Welcome to the GitHub Copilot Training Workshop! This repository is designed to help developers of all levels learn how to effectively use GitHub Copilot through structured lessons and hands-on labs. + +## Repository Structure + +### Lessons + +Lessons are conceptual and not tied to a specific programming language. They cover foundational topics, best practices, and real-world examples to help you understand and maximize the potential of GitHub Copilot. + +- **Location:** `lessons/` +- **Examples:** + - `01-github_copilot.md`: Overview of GitHub Copilot tools. + - `02-prompt_engineering.md`: Learn how to craft effective prompts. + - `03-design_patterns.md`: Explore design patterns with Copilot. + +### Labs + +Labs are hands-on exercises that allow you to apply what you've learned in the lessons. These are often language-specific and provide practical experience with GitHub Copilot. + +- **Location:** `lessons/01-github-copilot-tools/labs/` +- **Examples:** + - `01a-exploring-copilot-ask.md`: Hands-on with Copilot Ask. + - `01b-exploring-copilot-edit.md`: Hands-on with Copilot Edit. + - `01c-exploring-copilot-agent.md`: Hands-on with Copilot Agent. + +### Schedule + +The `_schedule/` folder contains the structured agenda for the workshop. It outlines the flow of lessons and labs to ensure a cohesive learning experience. + +- **Location:** `lessons/_schedule/` +- **Examples:** + - `00-copilot_setup.md`: Initial setup for the workshop. + - `01-github_copilot.md`: Introduction to GitHub Copilot. + +## How to Contribute + +1. **Adding Lessons:** + + - Create a new markdown file in the `lessons/` folder. + - Follow the format of existing lessons for consistency. + +2. **Adding Labs:** + + - Create a new markdown file in the `labs/` folder under the relevant lesson. + - Ensure the lab is hands-on and provides clear instructions. + +3. **Updating the Schedule:** + - Modify the `_schedule/` folder to include your new lesson or lab. + - Ensure the schedule maintains a logical flow. + +## Getting Started + +1. Clone this repository: + + ```bash + git clone https://github.com/your-repo/github-copilot-workshop.git + cd github-copilot-workshop + ``` + +2. Follow the setup instructions in `lessons/_schedule/00-copilot_setup.md`. + +3. Start with the first lesson: `lessons/_schedule/01-github_copilot.md`. + +## Feedback + +We welcome your feedback! Please open an issue or submit a pull request if you have suggestions for improving the workshop. + +--- From 79c7b7db8ff09edee0121db7e9a84e214fa30420 Mon Sep 17 00:00:00 2001 From: "swagath.bairi" Date: Tue, 20 May 2025 14:28:03 -0700 Subject: [PATCH 07/66] fixed typo --- .../DotnetCoreApi/DataAccess/AppDbContext.cs | 6 ++-- .../DataAccess/Entities/Category.cs | 2 +- .../DataAccess/Entities/Inventory.cs | 2 +- .../DataAccess/Entities/Product.cs | 2 +- .../DataAccess/Entities/ProductAttribute.cs | 2 +- .../DataAccess/Entities/ProductPrice.cs | 2 +- .../DataAccess/Entities/ProductReview.cs | 2 +- .../CategoryConfiguration.cs | 4 +-- .../InventoryConfiguration.cs | 4 +-- .../ProductAttributeConfiguration.cs | 4 +-- .../ProductConfiguration.cs | 4 +-- .../ProductPriceConfiguration.cs | 4 +-- .../ProductReviewConfiguration.cs | 4 +-- ...sproj => copilot-sample.DataAccess.csproj} | 0 .../DotnetCoreApi/copilot-instructions.md | 30 ++++++++++++++++++ .../Contracts/ICategoryService.cs | 4 +-- .../Contracts/IProductService.cs | 4 +-- .../Controllers/CategoryController.cs | 6 ++-- .../Controllers/ProductAttributeController.cs | 6 ++-- .../Controllers/ProductController.cs | 6 ++-- .../DBSetup.md | 0 .../Images/gh-cp-header.png | Bin .../Images/open-gh-cp-chat.png | Bin .../InventoryDb.png | Bin .../Models/CategoryMappings.cs | 4 +-- .../Models/Dtos/AddCategoryDto.cs | 2 +- .../Models/Dtos/CategoryDto.cs | 2 +- .../Models/Dtos/ProductAttributeDto.cs | 2 +- .../Models/Dtos/ProductDto.cs | 2 +- .../Program.cs | 4 +-- .../Properties/launchSettings.json | 0 .../Services/CategoryService.cs | 8 ++--- .../Services/ProductAttributeService.cs | 8 ++--- .../Services/ProductService.cs | 8 ++--- .../appsettings.Development.json | 0 .../appsettings.json | 0 .../copilot-sample.Api.csproj} | 6 ++-- .../copilot-sample.Api/copilot-sample.http | 6 ++++ .../dbschema.sql | 0 .../learning.md} | 19 +++++++++-- .../CategoryServiceTests.cs | 8 ++--- .../copilot-sample.Test.csproj} | 6 ++-- ...copoilot-sample.sln => copilot-sample.sln} | 11 +++++-- .../copoilot-sample.Api/copoilot-sample.http | 6 ---- .../React/copilot-instructions.md | 30 ++++++++++++++++++ 45 files changed, 155 insertions(+), 75 deletions(-) rename samples/SimpleFullStack/DotnetCoreApi/DataAccess/{copoilot-sample.DataAccess.csproj => copilot-sample.DataAccess.csproj} (100%) create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Contracts/ICategoryService.cs (87%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Contracts/IProductService.cs (86%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Controllers/CategoryController.cs (95%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Controllers/ProductAttributeController.cs (95%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Controllers/ProductController.cs (94%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/DBSetup.md (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Images/gh-cp-header.png (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Images/open-gh-cp-chat.png (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/InventoryDb.png (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Models/CategoryMappings.cs (95%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Models/Dtos/AddCategoryDto.cs (87%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Models/Dtos/CategoryDto.cs (87%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Models/Dtos/ProductAttributeDto.cs (93%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Models/Dtos/ProductDto.cs (96%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Program.cs (94%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Properties/launchSettings.json (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Services/CategoryService.cs (95%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Services/ProductAttributeService.cs (94%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/Services/ProductService.cs (96%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/appsettings.Development.json (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/appsettings.json (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api/copoilot-sample.Api.csproj => copilot-sample.Api/copilot-sample.Api.csproj} (77%) create mode 100644 samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.http rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api => copilot-sample.Api}/dbschema.sql (100%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Api/copoilot.md => copilot-sample.Api/learning.md} (91%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Test => copilot-sample.Test}/CategoryServiceTests.cs (97%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.Test/copoilot-sample.Test.csproj => copilot-sample.Test/copilot-sample.Test.csproj} (83%) rename samples/SimpleFullStack/DotnetCoreApi/{copoilot-sample.sln => copilot-sample.sln} (69%) delete mode 100644 samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http create mode 100644 samples/SimpleFullStack/React/copilot-instructions.md diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs index 8db5357..bd11109 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; -using copoilot_sample.DataAccess.Entities; -using copoilot_sample.DataAccess.EntityConfiguration; +using copilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.EntityConfiguration; -namespace copoilot_sample.DataAccess +namespace copilot_sample.DataAccess { public class AppDbContext : DbContext { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs index 6a0babf..13d354d 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.DataAccess.Entities +namespace copilot_sample.DataAccess.Entities { public class Category { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs index 8b0c0bb..42af7a4 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.DataAccess.Entities +namespace copilot_sample.DataAccess.Entities { public class Inventory { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs index 705f859..afe0daa 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.DataAccess.Entities +namespace copilot_sample.DataAccess.Entities { public class Product { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs index ecbc4ff..3e77407 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.DataAccess.Entities +namespace copilot_sample.DataAccess.Entities { public class ProductAttribute { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs index 1a514a1..167b2c3 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.DataAccess.Entities +namespace copilot_sample.DataAccess.Entities { public class ProductPrice { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs index 03adfd8..ac0a008 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.DataAccess.Entities +namespace copilot_sample.DataAccess.Entities { public class ProductReview { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs index 7da4bf9..e330f5f 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Entities; -namespace copoilot_sample.DataAccess.EntityConfiguration +namespace copilot_sample.DataAccess.EntityConfiguration { public class CategoryConfiguration : IEntityTypeConfiguration { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs index 129b42c..e17ef4e 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Entities; -namespace copoilot_sample.DataAccess.EntityConfiguration +namespace copilot_sample.DataAccess.EntityConfiguration { public class InventoryConfiguration : IEntityTypeConfiguration { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs index e3d1406..b8cf948 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Entities; -namespace copoilot_sample.DataAccess.EntityConfiguration +namespace copilot_sample.DataAccess.EntityConfiguration { public class ProductAttributeConfiguration : IEntityTypeConfiguration { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs index 84b17a8..8d693ba 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Entities; -namespace copoilot_sample.DataAccess.EntityConfiguration +namespace copilot_sample.DataAccess.EntityConfiguration { public class ProductConfiguration : IEntityTypeConfiguration { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs index 4e747e0..6f7d7cd 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Entities; -namespace copoilot_sample.DataAccess.EntityConfiguration +namespace copilot_sample.DataAccess.EntityConfiguration { public class ProductPriceConfiguration : IEntityTypeConfiguration { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs index 1dc23f5..2a7d363 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs @@ -1,8 +1,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Entities; -namespace copoilot_sample.DataAccess.EntityConfiguration +namespace copilot_sample.DataAccess.EntityConfiguration { public class ProductReviewConfiguration : IEntityTypeConfiguration { diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/copoilot-sample.DataAccess.csproj b/samples/SimpleFullStack/DotnetCoreApi/DataAccess/copilot-sample.DataAccess.csproj similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/copoilot-sample.DataAccess.csproj rename to samples/SimpleFullStack/DotnetCoreApi/DataAccess/copilot-sample.DataAccess.csproj diff --git a/samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md b/samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md new file mode 100644 index 0000000..d0b3994 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md @@ -0,0 +1,30 @@ +# Copilot Instructions for .NET Core API + +## Project Overview +This project is a backend API built with ASP.NET Core (.NET 8). It provides RESTful endpoints for business logic and data access. + +## Using GitHub Copilot Effectively +- Use Copilot to generate controller actions, service methods, and data access code. +- Ask Copilot to write unit tests for your controllers and services. +- Use Copilot to generate documentation comments for methods and classes. + +## Coding Conventions +- Follow C# naming conventions (PascalCase for classes/methods, camelCase for variables). +- Use dependency injection for services. +- Keep controllers thin; put business logic in services. +- Write XML documentation for public APIs. + +## Example Copilot Prompts +- "Generate a controller for managing orders with CRUD endpoints." +- "Write a unit test for the OrderService class." +- "Add XML documentation to the ProductController methods." +- "Suggest a LINQ query to filter orders by status." + +## Running and Testing +- Build: `dotnet build` +- Run: `dotnet run` +- Test: `dotnet test` + +## Additional Tips +- Use Copilot Chat for code explanations and refactoring suggestions. +- Use inline Copilot suggestions for quick code completions. diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/ICategoryService.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/ICategoryService.cs similarity index 87% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/ICategoryService.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/ICategoryService.cs index c369743..941bb8a 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/ICategoryService.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/ICategoryService.cs @@ -1,6 +1,6 @@ -using copoilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Models.Dtos; -namespace copoilot_sample.Api.Services +namespace copilot_sample.Api.Services { /// /// Interface for managing categories in the application. diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/IProductService.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/IProductService.cs similarity index 86% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/IProductService.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/IProductService.cs index 7009486..f06ae12 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Contracts/IProductService.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Contracts/IProductService.cs @@ -1,6 +1,6 @@ -using copoilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Models.Dtos; -namespace copoilot_sample.Api.Services +namespace copilot_sample.Api.Services { /// /// Interface for managing products in the application. diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/CategoryController.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/CategoryController.cs similarity index 95% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/CategoryController.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/CategoryController.cs index 9d41e69..9f05ef5 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/CategoryController.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/CategoryController.cs @@ -1,8 +1,8 @@ -using copoilot_sample.Api.Models.Dtos; -using copoilot_sample.Api.Services; +using copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; using Microsoft.AspNetCore.Mvc; -namespace copoilot_sample.Api.Controllers +namespace copilot_sample.Api.Controllers { [ApiController] [Route("api/[controller]")] diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductAttributeController.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductAttributeController.cs similarity index 95% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductAttributeController.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductAttributeController.cs index a34ba4c..03cbf00 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductAttributeController.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductAttributeController.cs @@ -1,8 +1,8 @@ -using copoilot_sample.Api.Models.Dtos; -using copoilot_sample.Api.Services; +using copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; using Microsoft.AspNetCore.Mvc; -namespace copoilot_sample.Api.Controllers +namespace copilot_sample.Api.Controllers { [ApiController] [Route("api/[controller]")] diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductController.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductController.cs similarity index 94% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductController.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductController.cs index 7c7d730..f379a41 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Controllers/ProductController.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Controllers/ProductController.cs @@ -1,8 +1,8 @@ -using copoilot_sample.Api.Models.Dtos; -using copoilot_sample.Api.Services; +using copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; using Microsoft.AspNetCore.Mvc; -namespace copoilot_sample.Api.Controllers +namespace copilot_sample.Api.Controllers { [ApiController] [Route("api/[controller]")] diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/DBSetup.md similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/DBSetup.md rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/DBSetup.md diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/gh-cp-header.png b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Images/gh-cp-header.png similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/gh-cp-header.png rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Images/gh-cp-header.png diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/open-gh-cp-chat.png b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Images/open-gh-cp-chat.png similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Images/open-gh-cp-chat.png rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Images/open-gh-cp-chat.png diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/InventoryDb.png b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/InventoryDb.png similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/InventoryDb.png rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/InventoryDb.png diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/CategoryMappings.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/CategoryMappings.cs similarity index 95% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/CategoryMappings.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/CategoryMappings.cs index dbe77ee..84197f7 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/CategoryMappings.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/CategoryMappings.cs @@ -1,7 +1,7 @@ using System.Linq.Expressions; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.DataAccess.Entities; -namespace copoilot_sample.Api.Models.Dtos +namespace copilot_sample.Api.Models.Dtos { public static class CategoryMappings { diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/AddCategoryDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/AddCategoryDto.cs similarity index 87% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/AddCategoryDto.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/AddCategoryDto.cs index 09380f4..ad57b8a 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/AddCategoryDto.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/AddCategoryDto.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.Api.Models.Dtos +namespace copilot_sample.Api.Models.Dtos { public class AddCategoryDto { diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/CategoryDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/CategoryDto.cs similarity index 87% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/CategoryDto.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/CategoryDto.cs index b2ca556..493be18 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/CategoryDto.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/CategoryDto.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.Api.Models.Dtos +namespace copilot_sample.Api.Models.Dtos { public class CategoryDto { diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductAttributeDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductAttributeDto.cs similarity index 93% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductAttributeDto.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductAttributeDto.cs index 1d3d3b4..6a396b0 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductAttributeDto.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductAttributeDto.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.Api.Models.Dtos +namespace copilot_sample.Api.Models.Dtos { public class ProductAttributeDto { diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductDto.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductDto.cs similarity index 96% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductDto.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductDto.cs index c511adf..820705b 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Models/Dtos/ProductDto.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Models/Dtos/ProductDto.cs @@ -1,4 +1,4 @@ -namespace copoilot_sample.Api.Models.Dtos +namespace copilot_sample.Api.Models.Dtos { public class ProductDto { diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Program.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Program.cs similarity index 94% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Program.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Program.cs index 8b54705..831dfc5 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Program.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Program.cs @@ -1,5 +1,5 @@ -using copoilot_sample.DataAccess; -using copoilot_sample.Api.Services; +using copilot_sample.DataAccess; +using copilot_sample.Api.Services; using Microsoft.EntityFrameworkCore; using System.Text.Json.Serialization; diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Properties/launchSettings.json b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Properties/launchSettings.json similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Properties/launchSettings.json rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Properties/launchSettings.json diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/CategoryService.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/CategoryService.cs similarity index 95% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/CategoryService.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/CategoryService.cs index a27c663..e485439 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/CategoryService.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/CategoryService.cs @@ -1,9 +1,9 @@ -using copoilot_sample.DataAccess; -using copoilot_sample.DataAccess.Entities; -using copoilot_sample.Api.Models.Dtos; +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using copilot_sample.Api.Models.Dtos; using Microsoft.EntityFrameworkCore; -namespace copoilot_sample.Api.Services +namespace copilot_sample.Api.Services { /// /// Service for managing categories in the application. diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductAttributeService.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductAttributeService.cs similarity index 94% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductAttributeService.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductAttributeService.cs index be8387c..82a1bb8 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductAttributeService.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductAttributeService.cs @@ -1,9 +1,9 @@ -using copoilot_sample.DataAccess; -using copoilot_sample.DataAccess.Entities; -using copoilot_sample.Api.Models.Dtos; +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using copilot_sample.Api.Models.Dtos; using Microsoft.EntityFrameworkCore; -namespace copoilot_sample.Api.Services +namespace copilot_sample.Api.Services { public class ProductAttributeService { diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductService.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductService.cs similarity index 96% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductService.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductService.cs index bac3192..ed78d16 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/Services/ProductService.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/Services/ProductService.cs @@ -1,9 +1,9 @@ -using copoilot_sample.Api.Models.Dtos; -using copoilot_sample.DataAccess; -using copoilot_sample.DataAccess.Entities; +using copilot_sample.Api.Models.Dtos; +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; using Microsoft.EntityFrameworkCore; -namespace copoilot_sample.Api.Services +namespace copilot_sample.Api.Services { /// /// Service for managing products in the application. diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.Development.json b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.Development.json similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.Development.json rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.Development.json diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.json b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.json similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/appsettings.json rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/appsettings.json diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj similarity index 77% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj index 2cbbb9e..4f0cc1b 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.Api.csproj +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj @@ -4,7 +4,7 @@ net8.0 enable enable - copoilot_sample + copilot_sample @@ -16,11 +16,11 @@ - + - + diff --git a/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.http b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.http new file mode 100644 index 0000000..a3e6b96 --- /dev/null +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.http @@ -0,0 +1,6 @@ +@copilot_sample_HostAddress = http://localhost:5147 + +GET {{copilot_sample_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/dbschema.sql b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/dbschema.sql similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/dbschema.sql rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/dbschema.sql diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/learning.md similarity index 91% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/learning.md index 9de6623..b55e365 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot.md +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/learning.md @@ -18,29 +18,34 @@ The GitHub Copilot chat window will open on the right side of the Visual Studio Open the Copilot chat thread and ask by clicking on the "Create new thread" button. -### Examples +### Examples in Ask mode 1. **Explain the code:** + ```text @workspace, Explain the DataAccess project in the solution. Also explain how it is being used and triggered. ``` 2. **Ask Copilot to implement a feature:** + ```text @workspace, In ProductService class, AddProductAsync method, add a check to see if the product already exists in the database. If it does, throw a custom exception named ProductAlreadyExistsException. ``` 3. **Create a controller and service:** + ```text @workspace, Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle DB operations and add it to DI. Also add necessary DTO models. ``` 4. **Ask Copilot about an exception:** + ```text Run GetProducts endpoint and check if there are any exceptions, and explore "Analyze with Copilot". ``` 5. **Generate integration code:** + ```text @workspace, I am trying to consume CategoryController endpoint in UI TypeScript project. It uses axios for HTTP calls, please generate TS code with interfaces. ``` @@ -49,23 +54,32 @@ Open the Copilot chat thread and ask by clicking on the "Create new thread" butt Click on the "Create new Edits thread" button to open the edit mode chat. -### Examples +### Examples for edit mode 1. **Document the code:** + ```text @workspace, Document the ProductAttributeService class and its methods. ``` 2. **Implement a feature:** + ```text @workspace, Create a controller for ProductReviews CRUD operations and add a service named ProductReviewService that implements IProductReviewService interface to handle DB operations and add it to DI. Also add necessary DTO models. ``` 3. **Create unit tests:** + ```text @workspace, Create unit tests for the ProductAttributeService class and its methods. ``` +4. **Build deployments:** + + ```text + I am planning to deploy the .net core api as a docker container on kubernetes, please help me generate the docker file. Also explain what each statement in the dockerfile does + ``` + ## Inline Chat with Copilot To open inline chat, select a code block and press `Alt + /`. @@ -74,6 +88,7 @@ To open inline chat, select a code block and press `Alt + /`. 1. **Explain a selected code block.** 2. **Ask Copilot to write code:** + ```text In ProductService class, AddProductAsync method, select the method body and click "Alt + /" to open inline chat. Please add a check if the Category already exists in the database. If it does, throw an exception. diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/CategoryServiceTests.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/CategoryServiceTests.cs similarity index 97% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/CategoryServiceTests.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/CategoryServiceTests.cs index c97d435..a7cee5b 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/CategoryServiceTests.cs +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/CategoryServiceTests.cs @@ -1,7 +1,7 @@ -using copoilot_sample.DataAccess; -using copoilot_sample.DataAccess.Entities; -using copoilot_sample.Api.Models.Dtos; -using copoilot_sample.Api.Services; +using copilot_sample.DataAccess; +using copilot_sample.DataAccess.Entities; +using copilot_sample.Api.Models.Dtos; +using copilot_sample.Api.Services; using FluentAssertions; using Microsoft.EntityFrameworkCore; diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/copoilot-sample.Test.csproj b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/copilot-sample.Test.csproj similarity index 83% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/copoilot-sample.Test.csproj rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/copilot-sample.Test.csproj index edf7fa9..76f3407 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Test/copoilot-sample.Test.csproj +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Test/copilot-sample.Test.csproj @@ -1,8 +1,8 @@ - + net8.0 - copoilot_sample.Test + copilot_sample.Test enable enable @@ -21,7 +21,7 @@ - + diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.sln b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln similarity index 69% rename from samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.sln rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln index 57968b3..1f748f0 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.sln +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln @@ -3,11 +3,16 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.13.35828.75 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copoilot-sample.Api", "copoilot-sample.Api\copoilot-sample.Api.csproj", "{8E754013-64CD-402F-A9D1-10D3CFE753D3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.Api", "copilot-sample.Api\copilot-sample.Api.csproj", "{8E754013-64CD-402F-A9D1-10D3CFE753D3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copoilot-sample.Test", "copoilot-sample.Test\copoilot-sample.Test.csproj", "{0AE92036-1C6A-41C7-A84D-FE4E1A579351}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.Test", "copilot-sample.Test\copilot-sample.Test.csproj", "{0AE92036-1C6A-41C7-A84D-FE4E1A579351}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copoilot-sample.DataAccess", "DataAccess\copoilot-sample.DataAccess.csproj", "{8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.DataAccess", "DataAccess\copilot-sample.DataAccess.csproj", "{8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" + ProjectSection(SolutionItems) = preProject + copilot-instructions.md = copilot-instructions.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http b/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http deleted file mode 100644 index e8c7cea..0000000 --- a/samples/SimpleFullStack/DotnetCoreApi/copoilot-sample.Api/copoilot-sample.http +++ /dev/null @@ -1,6 +0,0 @@ -@copoilot_sample_HostAddress = http://localhost:5147 - -GET {{copoilot_sample_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/samples/SimpleFullStack/React/copilot-instructions.md b/samples/SimpleFullStack/React/copilot-instructions.md new file mode 100644 index 0000000..a67f054 --- /dev/null +++ b/samples/SimpleFullStack/React/copilot-instructions.md @@ -0,0 +1,30 @@ +# Copilot Instructions for React Frontend + +## Project Overview +This project is a frontend application built with React and TypeScript. It interacts with backend APIs and provides a user interface for the application. + +## Using GitHub Copilot Effectively +- Use Copilot to generate React components, hooks, and utility functions. +- Ask Copilot to write unit and integration tests for components. +- Use Copilot to generate TypeScript interfaces and types. + +## Coding Conventions +- Use PascalCase for component names and camelCase for variables/functions. +- Prefer functional components and React hooks. +- Keep components small and focused. +- Use TypeScript for type safety. + +## Example Copilot Prompts +- "Create a React component for displaying a list of products." +- "Write a custom hook for fetching data from the API." +- "Generate a TypeScript interface for the Order object." +- "Write a unit test for the ProductList component using React Testing Library." + +## Running and Testing +- Install dependencies: `npm install` +- Run: `npm start` +- Test: `npm test` + +## Additional Tips +- Use Copilot Chat for code explanations and refactoring suggestions. +- Use inline Copilot suggestions for quick code completions. From d5d90bfbebbdbc96b489ef1b7329e7bde1c20f98 Mon Sep 17 00:00:00 2001 From: "swagath.bairi" Date: Tue, 20 May 2025 14:33:57 -0700 Subject: [PATCH 08/66] renamed dataaccess project root folder to copilot-sample.DataAccess for consistency --- .../DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj | 2 +- .../{DataAccess => copilot-sample.DataAccess}/AppDbContext.cs | 0 .../Entities/Category.cs | 0 .../Entities/Inventory.cs | 0 .../Entities/Product.cs | 0 .../Entities/ProductAttribute.cs | 0 .../Entities/ProductPrice.cs | 0 .../Entities/ProductReview.cs | 0 .../EntityConfiguration/CategoryConfiguration.cs | 0 .../EntityConfiguration/InventoryConfiguration.cs | 0 .../EntityConfiguration/ProductAttributeConfiguration.cs | 0 .../EntityConfiguration/ProductConfiguration.cs | 0 .../EntityConfiguration/ProductPriceConfiguration.cs | 0 .../EntityConfiguration/ProductReviewConfiguration.cs | 0 .../copilot-sample.DataAccess.csproj | 0 samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln | 2 +- 16 files changed, 2 insertions(+), 2 deletions(-) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/AppDbContext.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/Entities/Category.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/Entities/Inventory.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/Entities/Product.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/Entities/ProductAttribute.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/Entities/ProductPrice.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/Entities/ProductReview.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/EntityConfiguration/CategoryConfiguration.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/EntityConfiguration/InventoryConfiguration.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/EntityConfiguration/ProductAttributeConfiguration.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/EntityConfiguration/ProductConfiguration.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/EntityConfiguration/ProductPriceConfiguration.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/EntityConfiguration/ProductReviewConfiguration.cs (100%) rename samples/SimpleFullStack/DotnetCoreApi/{DataAccess => copilot-sample.DataAccess}/copilot-sample.DataAccess.csproj (100%) diff --git a/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj index 4f0cc1b..55c35db 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.Api/copilot-sample.Api.csproj @@ -20,7 +20,7 @@ - + diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/AppDbContext.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/AppDbContext.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/AppDbContext.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Category.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Category.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Category.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Inventory.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Inventory.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Inventory.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Product.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/Product.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/Product.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductAttribute.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductAttribute.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductAttribute.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductPrice.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductPrice.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductPrice.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductReview.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/Entities/ProductReview.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/Entities/ProductReview.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/CategoryConfiguration.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/CategoryConfiguration.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/CategoryConfiguration.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/InventoryConfiguration.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/InventoryConfiguration.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/InventoryConfiguration.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductAttributeConfiguration.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductConfiguration.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductConfiguration.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductConfiguration.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductPriceConfiguration.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductPriceConfiguration.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductPriceConfiguration.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductReviewConfiguration.cs similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/EntityConfiguration/ProductReviewConfiguration.cs rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/EntityConfiguration/ProductReviewConfiguration.cs diff --git a/samples/SimpleFullStack/DotnetCoreApi/DataAccess/copilot-sample.DataAccess.csproj b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/copilot-sample.DataAccess.csproj similarity index 100% rename from samples/SimpleFullStack/DotnetCoreApi/DataAccess/copilot-sample.DataAccess.csproj rename to samples/SimpleFullStack/DotnetCoreApi/copilot-sample.DataAccess/copilot-sample.DataAccess.csproj diff --git a/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln index 1f748f0..74e411a 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-sample.sln @@ -7,7 +7,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.Api", "copil EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.Test", "copilot-sample.Test\copilot-sample.Test.csproj", "{0AE92036-1C6A-41C7-A84D-FE4E1A579351}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.DataAccess", "DataAccess\copilot-sample.DataAccess.csproj", "{8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "copilot-sample.DataAccess", "copilot-sample.DataAccess\copilot-sample.DataAccess.csproj", "{8EFCEFC0-CD82-48B2-8E7C-CB56BA85BAE3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}" ProjectSection(SolutionItems) = preProject From c6ec11a37477abed2e7aa70d8aafae0767dd8893 Mon Sep 17 00:00:00 2001 From: "swagath.bairi" Date: Tue, 20 May 2025 16:39:25 -0700 Subject: [PATCH 09/66] added copilot best practices in lessons --- lessons/03-copoilot-best-practices.md | 70 +++++++++++++++++++ lessons/README.md | 2 +- .../DotnetCoreApi/copilot-instructions.md | 24 ++++--- 3 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 lessons/03-copoilot-best-practices.md diff --git a/lessons/03-copoilot-best-practices.md b/lessons/03-copoilot-best-practices.md new file mode 100644 index 0000000..387e842 --- /dev/null +++ b/lessons/03-copoilot-best-practices.md @@ -0,0 +1,70 @@ +# Copilot Best Practices + +## 1. Use It to Boost, Not Replace, Your Thinking + +## 2. Security & Ethics – Avoid Leaking Sensitive Data + +- **Do not** include secrets, API keys, or passwords in prompts. +- **Review licenses** for generated code that looks like it came from real projects. +- **Avoid copying** without understanding. + +## 3. Design Your Workflows Around Prompt Quality, Context Control, and Code Validation + +### a. Prompt Quality + +- Write clear and intentional prompts. +- Remove unused or misleading comments in code. +- Rewrite your prompts to generate different responses. If Copilot is not providing a helpful response, try rephrasing. + +### b. Control the Context + +- Use meaningful variable and function names. +- The quality of results from Copilot Chat may be degraded if very large files, or a large number of files, are used as context for a question. + - Break down your tasks. + - Keep functions short and readable. + - Comment clearly above complex logic. + +- **Ways to provide necessary Context:** + - Open relevant files and close irrelevant files to provide accurate context. + - Provide folder or workspace as context. + - Provide context from images, or by attaching files. + - In Copilot Chat, if a particular request is no longer helpful context, delete that request from the conversation. Alternatively, if none of the context of a particular conversation is helpful, start a new conversation. + - Provide context by writing clean and readable code. + - Always use well-descriptive naming conventions that help humans read code and AI to have more context. + - Use comments to provide context. + +- **Custom Instructions to GitHub Copilot:** + + 1. Different models at different times can generate code that does not take every detail in your solution into account, depending on the current context and model capacity. + - For example, open the file `src/Basket.API/Grpc/BasketService.cs` and press `Ctrl-Alt-I` to open Chat view and ask to "create unit tests for this class" and observe the generated code. + - In most cases, for .NET solutions, it will generate code using the `Moq` library for mocking dependencies. + + 2. You can create a custom instructions file to automatically add information to all questions you ask Copilot. + - Create a file `.github/copilot-instructions.md` and add instructions like "When mocking dependencies in tests, use NSubstitute." Close the file and retry the question. + - Observe two things: + - GitHub Copilot used two references: the file in focus and the custom instructions file. + - Dependencies are mocked using the `NSubstitute` library. + + 3. Since custom instructions consume model tokens, you should try to write [effective instructions](https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot#writing-effective-custom-instructions). + + 4. In VS Code, you can temporarily switch custom instructions on and off by using the Settings editor (shortcut `Ctrl + ,` (Linux/Windows) / `Command + ,` (Mac)). + - Type "instruction file" in the search box and select or clear the **Use Instruction Files** checkbox. + +### c. Code Validation + +Never blindly accept code, especially for security-critical or production systems. + +- **Ask for variants when needed.** +- **Review before you commit.** + - **Checklist:** + - Read line by line + - Check logic and edge cases + - Add docstrings/comments + - Run tests or static analysis + - Make it match your team’s standards + +- Use Copilot for repetitive, boilerplate tasks: + - Writing model classes (e.g., C# classes, TS interfaces) + - Setting up routes or endpoints + - Writing unit test scaffolds + - Generating repetitive validation or serialization logic diff --git a/lessons/README.md b/lessons/README.md index 38d6b04..eef95c0 100644 --- a/lessons/README.md +++ b/lessons/README.md @@ -12,7 +12,7 @@ Lessons are conceptual and not tied to a specific programming language. They cov - **Examples:** - `01-github_copilot.md`: Overview of GitHub Copilot tools. - `02-prompt_engineering.md`: Learn how to craft effective prompts. - - `03-design_patterns.md`: Explore design patterns with Copilot. + - `03-copilot-best-practices.md`: Explore best practices of Copilot. ### Labs diff --git a/samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md b/samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md index d0b3994..913c763 100644 --- a/samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md +++ b/samples/SimpleFullStack/DotnetCoreApi/copilot-instructions.md @@ -2,23 +2,31 @@ ## Project Overview This project is a backend API built with ASP.NET Core (.NET 8). It provides RESTful endpoints for business logic and data access. +Has 3 projects +1. copilot-sample.Api - ASP.NET Core Web API project. +2. copilot-sample.DataAccess - C# class library project to handle database context, repositories, and data models. +3. copilot-sample.Test - C# unit test project. -## Using GitHub Copilot Effectively -- Use Copilot to generate controller actions, service methods, and data access code. -- Ask Copilot to write unit tests for your controllers and services. -- Use Copilot to generate documentation comments for methods and classes. +## Technologies and libraries used +- ASP.NET Core 8 +- Entity Framework Core 8 +- Swagger / Swashbuckle (for API documentation) +- Microsoft.Extensions.DependencyInjection (for DI) +- C# +- xUnit (for testing) +- Moq and FluentAssertions (for mocking dependencies and assertions) +- Microsoft.EntityFrameworkCore.InMemory library for EF entity unit tests ## Coding Conventions - Follow C# naming conventions (PascalCase for classes/methods, camelCase for variables). - Use dependency injection for services. - Keep controllers thin; put business logic in services. -- Write XML documentation for public APIs. ## Example Copilot Prompts -- "Generate a controller for managing orders with CRUD endpoints." -- "Write a unit test for the OrderService class." +- "Generate a controller for managing categories with CRUD endpoints." +- "Write a unit test for the CategoryService class." - "Add XML documentation to the ProductController methods." -- "Suggest a LINQ query to filter orders by status." +- "Suggest a LINQ query to filter products by category." ## Running and Testing - Build: `dotnet build` From aa14b6f55f76f47ca1ffb23bc1652017b791b56d Mon Sep 17 00:00:00 2001 From: Derek Huynen Date: Mon, 26 May 2025 23:44:46 -0700 Subject: [PATCH 10/66] big update to lesson 01 and labs --- .../01-github-copilot-tools.md | 119 +- .../images/copilot-agent.png | Bin 0 -> 17813 bytes .../images/copilot-ask.png | Bin 0 -> 11529 bytes .../images/copilot-edit.png | Bin 0 -> 12234 bytes .../labs/01a-exploring-copilot-ask.md | 2 +- .../labs/01b-exploring-copilot-edit.md | 4 +- .../labs/01c-exploring-copilot-agent.md | 164 +- .../labs/01d-exploring-copilot-inline.md | 4 +- .../01a-exploring-copilot-ask-(react).md | 129 + .../01b-exploring-copilot-edit-(react).md | 131 + .../01c-exploring-copilot-agent-(react).md | 162 + .../01d-exploring-copilot-inline-(react).md | 139 + .../AzureFunctionC#/.gitignore | 264 - .../AzureFunctionC#/Model/Order.cs | 30 - .../AzureFunctionC#/Model/OrderResponse.cs | 19 - .../AzureFunctionC#/OrderFunctions.cs | 99 - .../AzureFunctionC#/Program.cs | 22 - .../Properties/launchSettings.json | 9 - .../AzureFunctionC#/Service/OrdersService.cs | 72 - .../Service/SecondOrdersService.cs | 0 .../AzureFunctionC#/api.csproj | 34 - .../SimpleFullStack/AzureFunctionC#/host.json | 12 - .../AzureFunctionC#/orders.json | 72 - samples/SimpleFullStack/React/.env.example | 4 - samples/SimpleFullStack/React/.eslintrc.cjs | 18 - samples/SimpleFullStack/React/.gitignore | 32 - samples/SimpleFullStack/React/.prettierignore | 3 - samples/SimpleFullStack/React/.prettierrc | 9 - samples/SimpleFullStack/React/README.md | 4 - .../React/copilot-instructions.md | 30 - samples/SimpleFullStack/React/index.html | 14 - samples/SimpleFullStack/React/package.json | 62 - .../React/public/Neudesic-N.svg | 12 - .../React/public/Neudesic_Logo.svg | 32 - .../React/public/ProSpec_AI.svg | 26 - .../React/public/ProSpec_AI_Chat.svg | 15 - samples/SimpleFullStack/React/src/App.tsx | 35 - .../React/src/assets/Neudesic-N.svg | 12 - .../React/src/assets/Neudesic_Logo.svg | 32 - .../React/src/assets/neudesic-logo-black.svg | 1 - .../React/src/assets/neudesic-n-logo.svg | 10 - .../React/src/components/FilterComponent.tsx | 82 - .../React/src/components/TabsPanels.tsx | 81 - .../components/generics/GenericDataGrid.tsx | 118 - .../src/components/generics/GenericForm.tsx | 43 - .../src/components/generics/GenericTabs.tsx | 68 - .../src/components/globals/GlobalModal.tsx | 102 - .../globals/GlobalSecondaryModal.tsx | 70 - .../components/globals/GlobalSidePanel.tsx | 32 - .../src/components/globals/GlobalSnackbar.tsx | 44 - .../src/components/layouts/PageContainer.tsx | 86 - .../src/constants/companyDropdownOptions.tsx | 28 - .../React/src/constants/mockCompanies.ts | 94 - .../controllers/ControllerDatePicker.tsx | 34 - .../controllers/ControllerRadioGroup.tsx | 93 - .../forms/controllers/ControllerSelect.tsx | 56 - .../controllers/ControllerSelectDrowpDown.tsx | 99 - .../forms/controllers/ControllerTextField.tsx | 64 - samples/SimpleFullStack/React/src/global.css | 3 - .../React/src/helpers/formatDate.ts | 6 - samples/SimpleFullStack/React/src/main.tsx | 15 - .../React/src/pages/CompanyPage.tsx | 47 - .../SimpleFullStack/React/src/pages/Home.tsx | 17 - .../React/src/pages/NoContentPage.tsx | 7 - .../src/pages/company/CompaniesTable.test.tsx | 82 - .../src/pages/company/CompaniesTable.tsx | 153 - .../React/src/pages/company/CompanyForm.tsx | 130 - .../React/src/pages/order/OrderCard.test.tsx | 19 - .../React/src/pages/order/OrderCard.tsx | 39 - .../pages/test/ColorTextComponent.test.txt | 26 - .../src/pages/test/ColorTextComponent.tsx | 35 - .../pages/test/CompanyAndIndustry.test.tsx | 44 - .../src/pages/test/CompanyAndIndustry.tsx | 37 - .../answer/AnswerColorTextComponent.test.tsx | 27 - .../test/answer/AnswerCompanyAndIndustry.tsx | 42 - .../React/src/routes/privateRoutes.tsx | 41 - .../React/src/service/axiosConfig.ts | 14 - .../React/src/service/crud/useDelete.ts | 67 - .../React/src/service/crud/useFetch.ts | 80 - .../React/src/service/crud/usePost.ts | 68 - .../React/src/service/crud/useUpsert.ts | 68 - .../React/src/service/hooks/useDebounce.ts | 15 - .../React/src/store/global/useGlobalModal.ts | 96 - .../store/global/useGlobalSecondaryModal.ts | 79 - .../src/store/global/useGlobalSidePanel.ts | 24 - .../src/store/global/useGlobalSnackbar.ts | 32 - .../React/src/store/useAppStore.ts | 11 - .../React/src/store/useCompanies.ts | 28 - .../React/src/store/useFormConfigs.ts | 11 - .../React/src/store/useTableSearch.ts | 24 - .../React/src/styles/themes.ts | 62 - .../React/src/types/company.ts | 19 - .../React/src/types/helperTypes.ts | 35 - .../SimpleFullStack/React/src/types/order.ts | 12 - .../React/src/types/theme.d.ts | 11 - .../React/staticwebapp.config.json | 13 - samples/SimpleFullStack/React/tsconfig.json | 11 - .../SimpleFullStack/React/tsconfig.node.json | 13 - .../SimpleFullStack/React/tsconfig.test.json | 7 - samples/SimpleFullStack/React/vite.config.ts | 27 - samples/SimpleFullStack/Web/.example.env | 1 + samples/SimpleFullStack/Web/.gitignore | 99 + samples/SimpleFullStack/Web/README.md | 132 + samples/SimpleFullStack/Web/eslint.config.js | 28 + samples/SimpleFullStack/Web/index.html | 30 + samples/SimpleFullStack/Web/package-lock.json | 6306 +++++++++++++++++ samples/SimpleFullStack/Web/package.json | 44 + .../Web/public/github-copilot-logo.svg | 16 + .../SimpleFullStack/Web/public/routes.json | 9 + samples/SimpleFullStack/Web/routes.json | 9 + samples/SimpleFullStack/Web/src/App.tsx | 33 + .../Web/src/config/constants.ts | 25 + .../SimpleFullStack/Web/src/config/routes.tsx | 38 + .../Web/src/config/theme/lightTheme.ts | 30 + .../Web/src/json/categories.json | 75 + .../Web/src/json/productAttributes.json | 230 + .../Web/src/json/products.json | 162 + samples/SimpleFullStack/Web/src/main.tsx | 9 + .../Web/src/services/axiosClient.ts | 13 + .../Web/src/store/useAppConfig.ts | 15 + .../SimpleFullStack/Web/src/types/Category.ts | 17 + .../SimpleFullStack/Web/src/types/Product.ts | 30 + .../Web/src/types/ProductAttribute.ts | 17 + .../components/boundaries/ErrorBoundary.tsx | 51 + .../ui/components/boundaries/LoadingPage.tsx | 13 + .../ui/components/boundaries/NotFoundPage.tsx | 18 + .../ui/components/bread_crumb/BreadCrumb.tsx | 42 + .../Web/src/ui/components/footer/Footer.tsx | 12 + .../global_modal.tsx/GlobalModal.tsx | 64 + .../global_modal.tsx/useGlobalModal.tsx | 64 + .../global_snackbar/GlobalSnackbar.tsx | 39 + .../global_snackbar/useGlobalSnackbar.ts | 41 + .../src/ui/components/layouts/MainLayout.tsx | 28 + .../Web/src/ui/components/nav/NavBar.tsx | 188 + .../Web/src/ui/components/nav/ThemeToggle.tsx | 29 + .../src/ui/components/product/ProductCard.tsx | 127 + .../ui/components/product/ProductFilter.tsx | 277 + .../ui/components/product/ProductsGrid.tsx | 62 + .../Web/src/ui/pages/home/HomePage.tsx | 16 + .../ui/pages/product/ProductDetailPage.tsx | 271 + .../Web/src/ui/pages/product/ProductsPage.tsx | 3 + .../src/ui/pages/product/ProductsPages.tsx | 189 + .../{React => Web}/src/vite-env.d.ts | 0 .../{React => Web}/tsconfig.app.json | 25 +- samples/SimpleFullStack/Web/tsconfig.json | 7 + .../SimpleFullStack/Web/tsconfig.node.json | 25 + samples/SimpleFullStack/Web/vite.config.ts | 29 + 147 files changed, 9768 insertions(+), 3849 deletions(-) create mode 100644 lessons/01-github-copilot-tools/images/copilot-agent.png create mode 100644 lessons/01-github-copilot-tools/images/copilot-ask.png create mode 100644 lessons/01-github-copilot-tools/images/copilot-edit.png create mode 100644 lessons/01-github-copilot-tools/labs/react/01a-exploring-copilot-ask-(react).md create mode 100644 lessons/01-github-copilot-tools/labs/react/01b-exploring-copilot-edit-(react).md create mode 100644 lessons/01-github-copilot-tools/labs/react/01c-exploring-copilot-agent-(react).md create mode 100644 lessons/01-github-copilot-tools/labs/react/01d-exploring-copilot-inline-(react).md delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/.gitignore delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/Model/Order.cs delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/Model/OrderResponse.cs delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/OrderFunctions.cs delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/Program.cs delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/Properties/launchSettings.json delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/Service/OrdersService.cs delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/Service/SecondOrdersService.cs delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/api.csproj delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/host.json delete mode 100644 samples/SimpleFullStack/AzureFunctionC#/orders.json delete mode 100644 samples/SimpleFullStack/React/.env.example delete mode 100644 samples/SimpleFullStack/React/.eslintrc.cjs delete mode 100644 samples/SimpleFullStack/React/.gitignore delete mode 100644 samples/SimpleFullStack/React/.prettierignore delete mode 100644 samples/SimpleFullStack/React/.prettierrc delete mode 100644 samples/SimpleFullStack/React/README.md delete mode 100644 samples/SimpleFullStack/React/copilot-instructions.md delete mode 100644 samples/SimpleFullStack/React/index.html delete mode 100644 samples/SimpleFullStack/React/package.json delete mode 100644 samples/SimpleFullStack/React/public/Neudesic-N.svg delete mode 100644 samples/SimpleFullStack/React/public/Neudesic_Logo.svg delete mode 100644 samples/SimpleFullStack/React/public/ProSpec_AI.svg delete mode 100644 samples/SimpleFullStack/React/public/ProSpec_AI_Chat.svg delete mode 100644 samples/SimpleFullStack/React/src/App.tsx delete mode 100644 samples/SimpleFullStack/React/src/assets/Neudesic-N.svg delete mode 100644 samples/SimpleFullStack/React/src/assets/Neudesic_Logo.svg delete mode 100644 samples/SimpleFullStack/React/src/assets/neudesic-logo-black.svg delete mode 100644 samples/SimpleFullStack/React/src/assets/neudesic-n-logo.svg delete mode 100644 samples/SimpleFullStack/React/src/components/FilterComponent.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/TabsPanels.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/generics/GenericDataGrid.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/generics/GenericForm.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/generics/GenericTabs.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/globals/GlobalModal.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/globals/GlobalSecondaryModal.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/globals/GlobalSidePanel.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/globals/GlobalSnackbar.tsx delete mode 100644 samples/SimpleFullStack/React/src/components/layouts/PageContainer.tsx delete mode 100644 samples/SimpleFullStack/React/src/constants/companyDropdownOptions.tsx delete mode 100644 samples/SimpleFullStack/React/src/constants/mockCompanies.ts delete mode 100644 samples/SimpleFullStack/React/src/forms/controllers/ControllerDatePicker.tsx delete mode 100644 samples/SimpleFullStack/React/src/forms/controllers/ControllerRadioGroup.tsx delete mode 100644 samples/SimpleFullStack/React/src/forms/controllers/ControllerSelect.tsx delete mode 100644 samples/SimpleFullStack/React/src/forms/controllers/ControllerSelectDrowpDown.tsx delete mode 100644 samples/SimpleFullStack/React/src/forms/controllers/ControllerTextField.tsx delete mode 100644 samples/SimpleFullStack/React/src/global.css delete mode 100644 samples/SimpleFullStack/React/src/helpers/formatDate.ts delete mode 100644 samples/SimpleFullStack/React/src/main.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/CompanyPage.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/Home.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/NoContentPage.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/company/CompaniesTable.test.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/company/CompaniesTable.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/company/CompanyForm.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/order/OrderCard.test.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/order/OrderCard.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.test.txt delete mode 100644 samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.test.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/test/answer/AnswerColorTextComponent.test.tsx delete mode 100644 samples/SimpleFullStack/React/src/pages/test/answer/AnswerCompanyAndIndustry.tsx delete mode 100644 samples/SimpleFullStack/React/src/routes/privateRoutes.tsx delete mode 100644 samples/SimpleFullStack/React/src/service/axiosConfig.ts delete mode 100644 samples/SimpleFullStack/React/src/service/crud/useDelete.ts delete mode 100644 samples/SimpleFullStack/React/src/service/crud/useFetch.ts delete mode 100644 samples/SimpleFullStack/React/src/service/crud/usePost.ts delete mode 100644 samples/SimpleFullStack/React/src/service/crud/useUpsert.ts delete mode 100644 samples/SimpleFullStack/React/src/service/hooks/useDebounce.ts delete mode 100644 samples/SimpleFullStack/React/src/store/global/useGlobalModal.ts delete mode 100644 samples/SimpleFullStack/React/src/store/global/useGlobalSecondaryModal.ts delete mode 100644 samples/SimpleFullStack/React/src/store/global/useGlobalSidePanel.ts delete mode 100644 samples/SimpleFullStack/React/src/store/global/useGlobalSnackbar.ts delete mode 100644 samples/SimpleFullStack/React/src/store/useAppStore.ts delete mode 100644 samples/SimpleFullStack/React/src/store/useCompanies.ts delete mode 100644 samples/SimpleFullStack/React/src/store/useFormConfigs.ts delete mode 100644 samples/SimpleFullStack/React/src/store/useTableSearch.ts delete mode 100644 samples/SimpleFullStack/React/src/styles/themes.ts delete mode 100644 samples/SimpleFullStack/React/src/types/company.ts delete mode 100644 samples/SimpleFullStack/React/src/types/helperTypes.ts delete mode 100644 samples/SimpleFullStack/React/src/types/order.ts delete mode 100644 samples/SimpleFullStack/React/src/types/theme.d.ts delete mode 100644 samples/SimpleFullStack/React/staticwebapp.config.json delete mode 100644 samples/SimpleFullStack/React/tsconfig.json delete mode 100644 samples/SimpleFullStack/React/tsconfig.node.json delete mode 100644 samples/SimpleFullStack/React/tsconfig.test.json delete mode 100644 samples/SimpleFullStack/React/vite.config.ts create mode 100644 samples/SimpleFullStack/Web/.example.env create mode 100644 samples/SimpleFullStack/Web/.gitignore create mode 100644 samples/SimpleFullStack/Web/README.md create mode 100644 samples/SimpleFullStack/Web/eslint.config.js create mode 100644 samples/SimpleFullStack/Web/index.html create mode 100644 samples/SimpleFullStack/Web/package-lock.json create mode 100644 samples/SimpleFullStack/Web/package.json create mode 100644 samples/SimpleFullStack/Web/public/github-copilot-logo.svg create mode 100644 samples/SimpleFullStack/Web/public/routes.json create mode 100644 samples/SimpleFullStack/Web/routes.json create mode 100644 samples/SimpleFullStack/Web/src/App.tsx create mode 100644 samples/SimpleFullStack/Web/src/config/constants.ts create mode 100644 samples/SimpleFullStack/Web/src/config/routes.tsx create mode 100644 samples/SimpleFullStack/Web/src/config/theme/lightTheme.ts create mode 100644 samples/SimpleFullStack/Web/src/json/categories.json create mode 100644 samples/SimpleFullStack/Web/src/json/productAttributes.json create mode 100644 samples/SimpleFullStack/Web/src/json/products.json create mode 100644 samples/SimpleFullStack/Web/src/main.tsx create mode 100644 samples/SimpleFullStack/Web/src/services/axiosClient.ts create mode 100644 samples/SimpleFullStack/Web/src/store/useAppConfig.ts create mode 100644 samples/SimpleFullStack/Web/src/types/Category.ts create mode 100644 samples/SimpleFullStack/Web/src/types/Product.ts create mode 100644 samples/SimpleFullStack/Web/src/types/ProductAttribute.ts create mode 100644 samples/SimpleFullStack/Web/src/ui/components/boundaries/ErrorBoundary.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/boundaries/LoadingPage.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/boundaries/NotFoundPage.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/bread_crumb/BreadCrumb.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/GlobalModal.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/useGlobalModal.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/global_snackbar/GlobalSnackbar.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/global_snackbar/useGlobalSnackbar.ts create mode 100644 samples/SimpleFullStack/Web/src/ui/components/layouts/MainLayout.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/nav/NavBar.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/nav/ThemeToggle.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/product/ProductCard.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/product/ProductFilter.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/components/product/ProductsGrid.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/pages/home/HomePage.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/pages/product/ProductDetailPage.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/pages/product/ProductsPage.tsx create mode 100644 samples/SimpleFullStack/Web/src/ui/pages/product/ProductsPages.tsx rename samples/SimpleFullStack/{React => Web}/src/vite-env.d.ts (100%) rename samples/SimpleFullStack/{React => Web}/tsconfig.app.json (52%) create mode 100644 samples/SimpleFullStack/Web/tsconfig.json create mode 100644 samples/SimpleFullStack/Web/tsconfig.node.json create mode 100644 samples/SimpleFullStack/Web/vite.config.ts diff --git a/lessons/01-github-copilot-tools/01-github-copilot-tools.md b/lessons/01-github-copilot-tools/01-github-copilot-tools.md index 3ecb486..9e2db6e 100644 --- a/lessons/01-github-copilot-tools/01-github-copilot-tools.md +++ b/lessons/01-github-copilot-tools/01-github-copilot-tools.md @@ -8,51 +8,79 @@ GitHub Copilot provides developers with powerful AI-assisted coding tools design ### 1. GitHub Copilot Ask -Engage interactively with Copilot to ask questions, request code suggestions, and optimize your code directly within a conversational interface. Copilot Ask is ideal for understanding existing code, exploring implementation strategies, and generating complex code snippets through conversational prompts. +GitHub Copilot Ask provides a simple yet powerful way to get quick answers to your programming questions without interrupting your workflow. Simply highlight code, ask a question in the Chat window, and receive immediate guidance. Copilot Ask operates as a "quick gut check" that helps you understand code, solve problems, or learn new concepts without making any changes to your codebase. + +This mode leverages your current editor context to provide highly relevant answers - explaining what code does, suggesting testing approaches, providing code snippets, or addressing edge cases. Think of it as having an expert programmer quietly whispering helpful advice in your ear. + +![Github Copilot Ask](images/copilot-ask.png 'GitHub Copilot Ask') **Example use cases:** -- Clarifying the logic of unfamiliar or complex code. -- Requesting code examples or implementations of specific features. -- Improving existing code with optimization suggestions. +- Understanding unfamiliar or complex code functionality +- Getting guidance on how to test specific code blocks +- Learning how to use specific libraries or frameworks +- Optimizing queries or algorithms for better performance +- Refreshing your knowledge on programming concepts (like closures in JavaScript) +- Finding the right syntax for specific programming tasks +- Getting unstuck on problems without committing to architectural changes + +**Labs:** -- **Lab:** [Exploring GitHub Copilot Chat](labs/01a-exploring-copilot-ask.md) +- [Exploring GitHub Copilot Chat](labs/01a-exploring-copilot-ask.md) (C#/.NET) +- [Exploring GitHub Copilot Chat with React]() (React) ### 2. GitHub Copilot Edit -Use Copilot Edit to perform extensive refactoring and optimizations across larger codebases by providing comprehensive context-aware edits. Copilot Edit analyzes the entire file or multiple files to suggest impactful, cohesive changes, significantly enhancing code maintainability and readability. +GitHub Copilot Edit enables you to apply precise modifications to specific files or sections within your codebase using natural language instructions. This tool excels when you need targeted changes to a well-defined set of files rather than extensive modifications across your entire project. Simply highlight the code you want to change, provide instructions like "add error handling" or "refactor using async/await," and Copilot rewrites the code for you – while always showing you the diff for review before any changes are saved. + +What makes Edit mode powerful is that you maintain full control. Copilot does the work, but you get the final say. You can also enhance its effectiveness by providing custom instructions that teach Copilot your team's coding standards, style preferences, and documentation requirements. + +![Github Copilot Edit](images/copilot-edit.png 'GitHub Copilot Edit') **Example use cases:** -- Refactoring legacy code to adhere to modern coding standards. -- Updating methods or classes across multiple files to ensure consistency. -- Optimizing large code blocks or implementing significant logic changes. +- Making controlled changes to a specific subset of your codebase +- Refactoring code with modern patterns while preserving the rest of the system +- Implementing targeted optimizations in performance-critical sections +- Adding error handling or logging to existing implementations +- Applying consistent patterns to related files without affecting other components +- Working in brownfield applications where you need surgical precision -- **Lab:** [Exploring Github Copilot Edit](labs/01b-exploring-copilot-edit.md) +**Labs:** + +- [Exploring GitHub Copilot Edit](labs/01b-exploring-copilot-edit.md) (C#/.NET) +- [Exploring GitHub Copilot Edit with React]() (React) ### 3. GitHub Copilot Agent -Leverage Copilot Agent to automate repetitive tasks and workflows, integrating seamlessly within your development environment. This tool acts as an intelligent assistant, automating tasks such as generating boilerplate code, managing dependencies, and routine maintenance. +GitHub Copilot Agent represents the most powerful mode in Copilot Chat, enabling autonomous planning and execution across your entire project. With Agent mode, you provide a high-level prompt and Copilot independently selects the right files, runs necessary tools or terminal commands, and applies code edits until the task is complete. Unlike Edit mode, Agent analyzes related code and identifies additional changes needed across the project to maintain consistency. + +What distinguishes Agent mode is its ability to work autonomously - applying edits automatically rather than waiting for explicit approval at each step, while still surfacing potentially risky commands for review. This creates a continuous-edit "driver" model where you define the goal and Copilot executes updates without interruption. +For maximum effectiveness, Agent mode works best with custom instructions that define your project structure, coding standards, and other guidelines. These instructions provide a stronger foundation for Copilot to work from, resulting in more consistent and aligned outcomes across multiple sessions. + +![ +GitHub Copilot Agent +](images/copilot-agent.png 'GitHub Copilot Agent') **Example use cases:** -- Automatically generating boilerplate for new projects or components. -- Managing dependency updates and routine environment configurations. -- Automating code reviews and routine coding checks. +- Building complete features from high-level descriptions +- Fixing complex bugs that require changes across multiple files +- Creating new files and scaffolding entire sections of an application +- Implementing architectural changes that affect multiple components +- Setting up new projects based on README specifications or requirements documents +- Refactoring code while maintaining consistent patterns throughout the codebase + +**Labs:** -- **Lab:** [Automating Tasks with Copilot Agent](labs/01c-exploring-copilot-agent.md) +- [Automating Tasks with Copilot Agent](labs/01c-exploring-copilot-agent.md) (C#/.NET) +- [Automating Tasks with Copilot Agent in eShop]() (eShop microservices) +- [Automating Tasks with Copilot Agent in React]() (React) ### 4. GitHub Copilot Inline Enhance productivity by using inline prompts directly within your editor, enabling quick code changes, method generation, and small incremental improvements without leaving the coding context. This approach is ideal for quick, iterative coding and minor adjustments that don't require extensive context switching. -**When to use:** - -- When making focused changes to a specific section of code -- During active development sessions where flow state is important -- For quick prototyping or iterative improvements to existing code -- When you need immediate suggestions without breaking your concentration - **Example use cases:** - Quickly renaming methods or variables across a single file @@ -64,27 +92,15 @@ Enhance productivity by using inline prompts directly within your editor, enabli - Implementing interface methods or abstract class implementations - Creating data models or DTOs based on existing patterns in your codebase -**Azure-specific examples:** - -- Quickly adding Azure SDK authentication code snippets directly in your editor -- Generating Azure Storage or Cosmos DB access code inline where needed -- Converting local configuration to Azure App Configuration or Key Vault references -- Adding Azure Monitor Application Insights telemetry to existing methods +**Labs:** -- **Lab:** [Exploring Github Copilot Inline](labs/01d-exploring-copilot-inline.md) +- [Exploring GitHub Copilot Inline](labs/01d-exploring-copilot-inline.md) (C#/.NET) +- [Exploring GitHub Copilot Inline with React]() (React) ### 5. GitHub Copilot Website Utilize the Copilot Web interface to explore suggestions, manage preferences, and gain insights into your coding habits and productivity. This centralized interface provides a comprehensive dashboard for reviewing usage analytics, setting global preferences, accessing educational resources, and managing your Copilot subscription. -**When to use:** - -- For managing subscription details and billing information -- To review personal usage metrics and productivity insights -- When setting up global preferences that apply across all development environments -- To access educational materials and latest updates about Copilot capabilities -- For providing feedback on Copilot's performance and suggestions - **Example use cases:** - Reviewing coding suggestions outside of your IDE @@ -96,27 +112,14 @@ Utilize the Copilot Web interface to explore suggestions, manage preferences, an - Reviewing historical usage statistics to optimize your development workflow - Providing feedback to the GitHub team on Copilot's suggestions -**Azure-specific examples:** +**Labs:** -- Accessing tailored Azure best practices resources through your Copilot dashboard -- Reviewing Azure-related code quality metrics in your Copilot analytics -- Managing Azure-specific Copilot custom instructions through the web interface -- Exploring additional Azure development resources linked from the Copilot dashboard - -- **Lab:** [Navigating Copilot's Web Interface](labs/01e-github-copilot-website.md) +- [Navigating Copilot's Web Interface](labs/01e-github-copilot-website.md) ### 6. GitHub Copilot CLI Streamline command-line operations by generating shell commands and automating tasks directly from your terminal. Copilot CLI transforms natural language descriptions into powerful command-line instructions, making complex operations accessible without requiring memorization of syntax or extensive documentation lookups. -**When to use:** - -- For executing complex or infrequently used commands -- When working in terminal-heavy workflows -- For automating repetitive command-line tasks -- When you need to construct complex pipelines or data transformations -- If you're unfamiliar with command syntax for specific tools or platforms - **Example use cases:** - Generating complex git commands for repository management (e.g., interactive rebasing, complex merges) @@ -128,16 +131,8 @@ Streamline command-line operations by generating shell commands and automating t - Creating Docker and Kubernetes management commands - Formulating complex data transformation pipelines using tools like jq or yq -**Azure-specific examples:** - -- Generating Azure CLI (az) commands for resource management -- Creating complex Azure Resource Manager (ARM) deployment commands -- Building Azure DevOps pipeline definitions and commands -- Automating Azure resource monitoring and maintenance tasks -- Generating secure connection strings and authentication commands -- Creating Azure Kubernetes Service (AKS) management commands -- Developing multi-stage deployment scripts for Azure environments +**Labs:** -- **Lab:** [Mastering GitHub Copilot CLI](#) Coming Soon +- [Mastering GitHub Copilot CLI](#) (Coming Soon) --- diff --git a/lessons/01-github-copilot-tools/images/copilot-agent.png b/lessons/01-github-copilot-tools/images/copilot-agent.png new file mode 100644 index 0000000000000000000000000000000000000000..469690dccc0485b4d79be8ee09bb9d0eef8291fc GIT binary patch literal 17813 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 literal 0 HcmV?d00001 diff --git a/lessons/01-github-copilot-tools/images/copilot-edit.png b/lessons/01-github-copilot-tools/images/copilot-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..79863b26bb9f86d96ae8d1968f71c56e791e33d0 GIT binary patch literal 12234 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 literal 0 HcmV?d00001 diff --git a/lessons/01-github-copilot-tools/labs/01a-exploring-copilot-ask.md b/lessons/01-github-copilot-tools/labs/01a-exploring-copilot-ask.md index 8947c61..313f523 100644 --- a/lessons/01-github-copilot-tools/labs/01a-exploring-copilot-ask.md +++ b/lessons/01-github-copilot-tools/labs/01a-exploring-copilot-ask.md @@ -28,7 +28,7 @@ Developers, QA testers, DevOps engineers, and Technical Writers. ### 2. Ask Contextual Questions -- Browse to `src/Ordering.API/Apis` folder and open the `OrdersApi.cs` file. +- Browse to `samples/Orders/Orders.Api/Controllers` folder and open the `OrderController.cs` file. - In the Copilot Chat prompt, type: ```text diff --git a/lessons/01-github-copilot-tools/labs/01b-exploring-copilot-edit.md b/lessons/01-github-copilot-tools/labs/01b-exploring-copilot-edit.md index e40c808..afd5410 100644 --- a/lessons/01-github-copilot-tools/labs/01b-exploring-copilot-edit.md +++ b/lessons/01-github-copilot-tools/labs/01b-exploring-copilot-edit.md @@ -23,7 +23,7 @@ Developers, QA testers, DevOps engineers, and Technical Writers working with med ### 1. Reset Your Code - Undo or discard any previous changes made to the `Order` class (if following from a previous lab). -- Ensure you have a clean version of `Order.cs` open for editing. +- Ensure you have a clean version of `samples/Orders/Orders.Domain/Order.cs` open for editing. ### 2. Launch Copilot Edit @@ -34,7 +34,7 @@ Developers, QA testers, DevOps engineers, and Technical Writers working with med ### 3. Add the File to Context -- Click `+ Add Files` and select the `Order.cs` file from your project to add it to the edit context. +- Click `+ Add Files` and select the `samples/Orders/Orders.Domain/Order.cs` file from your project to add it to the edit context. ### 4. Issue a Refactoring Prompt diff --git a/lessons/01-github-copilot-tools/labs/01c-exploring-copilot-agent.md b/lessons/01-github-copilot-tools/labs/01c-exploring-copilot-agent.md index e222323..e9167a6 100644 --- a/lessons/01-github-copilot-tools/labs/01c-exploring-copilot-agent.md +++ b/lessons/01-github-copilot-tools/labs/01c-exploring-copilot-agent.md @@ -1 +1,163 @@ -//TODO: UPDATE +### Lab: Automating Tasks with GitHub Copilot Agent in eShop + +## Overview + +**Goal:** +Learn how to leverage GitHub Copilot Agent to autonomously implement complex features and architectural changes across the eShop microservices application. + +**Estimated Duration:** +25-30 minutes + +**Audience:** +.NET developers, backend engineers, and architects working with microservice-based applications. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Chat with Agent capabilities +- Access to the eShop sample project + +## Lab Description + +GitHub Copilot Agent represents the most powerful mode in Copilot Chat, enabling autonomous planning and execution across your entire project. With Agent mode, you provide a high-level prompt and Copilot independently selects the right files, runs necessary tools or terminal commands, and applies code edits until the task is complete. Unlike Edit mode, Agent analyzes related code and identifies additional changes needed across the project to maintain consistency. + +What distinguishes Agent mode is its ability to work autonomously - applying edits automatically rather than waiting for explicit approval at each step, while still surfacing potentially risky commands for review. This creates a continuous-edit "driver" model where you define the goal and Copilot executes updates without interruption. + +For maximum effectiveness, Agent mode works best with custom instructions that define your project structure, coding standards, and other guidelines. These instructions provide a stronger foundation for Copilot to work from, resulting in more consistent and aligned outcomes across multiple sessions. + +## Lab Steps + +### 1. Launch Copilot Agent + +- Open Visual Studio Code with the eShop project. +- Navigate to the Copilot Chat panel by clicking the Copilot icon in the activity bar or using the keyboard shortcut `Ctrl+Shift+I` (Windows/Linux) or `Cmd+Shift+I` (Mac). +- Select the "Agent" tab from the top of the chat interface. + +### 2. Implement a Complete Feature: Order Tracking + +Ask Copilot Agent to implement a complete order tracking feature for the eShop application with this prompt: + +``` +Implement an order tracking feature for the eShop application. The feature should: +1. Add a new endpoint to the Ordering.API to get tracking information +2. Create a new TrackingInfo entity in the Ordering.Domain +3. Update the Order aggregate to include tracking information +4. Add the necessary repository methods and service logic +5. Ensure the feature works with the existing architecture and patterns +``` + +Observe as Copilot Agent: + +- Analyzes the existing code architecture in the project +- Identifies all necessary files to modify across multiple projects +- Creates new entities and updates existing ones +- Adds new endpoints and services +- Ensures consistency with the existing domain-driven design patterns + +Review the changes and test the new order tracking functionality. + +### 3. Enhance Error Handling Across Microservices + +Ask Copilot Agent to implement comprehensive error handling with the following prompt: + +``` +Enhance error handling across the eShop microservices by: +1. Implementing a consistent exception handling middleware in all API projects +2. Creating custom exception types for domain-specific errors +3. Adding proper logging with correlation IDs across service boundaries +4. Ensuring all API endpoints return standardized error responses +5. Implementing retry policies for transient failures in service-to-service communication +``` + +Observe as Copilot Agent: + +- Identifies common patterns across microservices +- Creates shared exception types and middleware +- Implements consistent logging +- Adds retry policies using Polly or similar libraries +- Updates API endpoints to use standardized error responses + +### 4. Implement Performance Monitoring + +Prompt Copilot Agent to add performance monitoring to the application: + +``` +Add performance monitoring to the eShop application by: +1. Implementing Application Insights integration across all microservices +2. Adding custom metrics for key business operations +3. Creating performance counters for database operations +4. Implementing distributed tracing across service boundaries +5. Adding health checks with appropriate degradation responses +``` + +Watch as Copilot Agent: + +- Installs necessary NuGet packages +- Configures Application Insights +- Implements custom metrics and telemetry +- Sets up distributed tracing +- Adds health check endpoints with appropriate logic + +### 5. Upgrade to .NET 8 Features + +Ask Copilot Agent to modernize the codebase with this prompt: + +``` +Upgrade the eShop application to take advantage of .NET 8 features by: +1. Updating project files and dependencies +2. Implementing minimal API improvements where applicable +3. Utilizing the new rate limiting features +4. Adding AOT compilation support where beneficial +5. Implementing the new identity features for better security +``` + +Observe as Copilot Agent: + +- Updates project files and NuGet packages +- Refactors code to use new .NET 8 features +- Implements rate limiting +- Configures AOT compilation settings +- Enhances identity and security features + +### 6. Implement a New Payment Gateway + +Challenge Copilot Agent with a complex integration task: + +``` +Implement a new payment gateway integration for the eShop application: +1. Create a new payment service provider interface +2. Implement a concrete provider for Stripe payments +3. Update the ordering process to support multiple payment providers +4. Add appropriate unit and integration tests +5. Ensure the implementation follows clean architecture principles +``` + +Watch as Copilot Agent: + +- Creates the necessary interfaces and implementations +- Integrates with the existing ordering process +- Adds unit and integration tests +- Ensures proper separation of concerns +- Maintains clean architecture principles + +## Best Practices for Using Copilot Agent with .NET Microservices + +- **Provide Architecture Context:** Mention key architectural patterns like DDD, CQRS, or event sourcing. +- **Break Large Tasks into Steps:** For complex features, guide Copilot with a numbered list of steps. +- **Specify Technology Preferences:** Mention specific technologies like Entity Framework, MediatR, or FluentValidation. +- **Review Changes Incrementally:** Pause between major feature additions to review and test. +- **Ask for Documentation:** Request that Copilot add XML documentation comments for new APIs. +- **Guide Code Style:** Mention preferences for naming conventions, exception handling, or logging approaches. + +## Summary + +By completing this lab, you've learned to: + +- Use GitHub Copilot Agent to implement complete features autonomously +- Enhance error handling and performance monitoring across microservices +- Upgrade applications to use new framework features +- Implement complex integrations with external services +- Maintain architectural consistency during significant changes + +These capabilities demonstrate how GitHub Copilot Agent can dramatically accelerate development by handling complex, multi-file changes that would typically require significant manual effort and coordination across multiple microservices. diff --git a/lessons/01-github-copilot-tools/labs/01d-exploring-copilot-inline.md b/lessons/01-github-copilot-tools/labs/01d-exploring-copilot-inline.md index 8b518a5..4896c84 100644 --- a/lessons/01-github-copilot-tools/labs/01d-exploring-copilot-inline.md +++ b/lessons/01-github-copilot-tools/labs/01d-exploring-copilot-inline.md @@ -1,4 +1,4 @@ -### Lab: Exploring GitHub Copilot Edit +### Lab: Exploring GitHub Copilot Inline ## Overview @@ -22,7 +22,7 @@ Developers looking to boost their productivity through fast, context-aware code ### 1. Open Target File -- Navigate to the `samples/eshop/src/Ordering.Domain/AggregatesModel/OrderAggregate` folder in Visual Studio Code. +- Navigate to the `samples/Orders/Orders.Domain` folder in Visual Studio Code. - Open the `Order.cs` file for editing. ### 2. Rename the Method diff --git a/lessons/01-github-copilot-tools/labs/react/01a-exploring-copilot-ask-(react).md b/lessons/01-github-copilot-tools/labs/react/01a-exploring-copilot-ask-(react).md new file mode 100644 index 0000000..9680c4f --- /dev/null +++ b/lessons/01-github-copilot-tools/labs/react/01a-exploring-copilot-ask-(react).md @@ -0,0 +1,129 @@ +### Lab: Exploring GitHub Copilot Ask with React + +## Overview + +**Goal:** +Learn how to use GitHub Copilot Ask to explore, understand, and improve your React codebase by asking natural language questions directly within Visual Studio Code. + +**Estimated Duration:** +15-20 minutes + +**Audience:** +React developers, UI/UX engineers, and front-end specialists. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Chat (requires a Copilot subscription) +- Access to the SimpleFullStack React project + +## Lab Steps + +### 1. Launch Copilot Chat + +- Open Visual Studio Code. +- Navigate to the Copilot Chat icon in the activity bar or use the shortcut `Ctrl + Alt + I` (Windows/Linux) or `Cmd + Option + I` (Mac). +- Ensure the model is set to **GPT-4o** in the settings for best results. + +### 2. Ask Contextual Questions About the React Component + +- Browse to `samples/SimpleFullStack/Web/src/ui/components/product/ProductCard.tsx` and open the file. +- In the Copilot Chat prompt, type: + +```text +How does this ProductCard component work and what props does it accept? +``` + +Observe the GitHub Copilot response explaining the component structure and props. + +Continue the conversation by asking a follow-up question: + +```text +How can I improve the performance of this component? +``` + +Observe and review the suggested improvements, which might include: + +- Memoizing the component with React.memo +- Using useMemo for computed values like imageUrl +- Avoiding unnecessary re-renders + +### 3. Understand the Application Architecture + +- Navigate to `samples/SimpleFullStack/Web/src/App.tsx` +- Ask Copilot: + +```text +Explain the structure of this React application and the libraries it uses. +``` + +Observe how Copilot explains the overall architecture, routing setup, and integration with Material UI and React Query. + +### 4. Get Help with API Integration + +- Open `samples/SimpleFullStack/Web/src/services/axiosClient.ts` +- Ask Copilot: + +```text +How can I add authentication headers to all API requests in this service? +``` + +Review Copilot's suggestions for implementing: + +- Authorization header setup +- Request/response interceptors for handling tokens +- Error handling for authentication failures + +### 5. Ask for New Code + +Type the following prompt in Copilot Chat: + +```text +Generate a new React hook called useProductSearch that filters products based on a search term. +``` + +Observe how Copilot suggests a complete custom hook implementation. You can: + +- Copy the code +- Insert at cursor +- Apply in editor + +### 6. Ask for Component Improvements + +Try a prompt such as: + +```text +Add a price comparison feature to the ProductCard component that shows if the current price is lower than the original price. +``` + +Review the code Copilot suggests to implement this feature. + +### 7. Request Testing Code + +Ask Copilot: + +```text +Write unit tests for the ProductCard component using React Testing Library. +``` + +Observe how Copilot generates comprehensive test cases for the component. + +## Best Practices + +- **Be Specific About React Concepts:** Mention specific React hooks, patterns, or libraries when asking questions. +- **Ask About Component Relationships:** Understanding how components interact is crucial in React applications. +- **Request Performance Optimizations:** React rendering optimization is a strength of Copilot. +- **Break Down Complex Features:** For larger feature requests, break them into smaller, focused prompts. +- **Try / Commands:** Use `/explain`, `/generate`, `/tests`, etc., to streamline specific actions. + +## Summary + +By completing this lab, you've learned to: + +- Ask Copilot natural language questions about React components. +- Generate custom hooks and functional components. +- Request performance optimizations and test code. +- Understand how to leverage Copilot for React-specific tasks. + +These techniques will help you incorporate GitHub Copilot Chat into your daily React development workflow effectively. diff --git a/lessons/01-github-copilot-tools/labs/react/01b-exploring-copilot-edit-(react).md b/lessons/01-github-copilot-tools/labs/react/01b-exploring-copilot-edit-(react).md new file mode 100644 index 0000000..af1c209 --- /dev/null +++ b/lessons/01-github-copilot-tools/labs/react/01b-exploring-copilot-edit-(react).md @@ -0,0 +1,131 @@ +### Lab: Exploring GitHub Copilot Edit with React + +## Overview + +**Goal:** +Use GitHub Copilot Edit to refactor, enhance, and optimize React components with natural language prompts, making precise modifications to your React codebase. + +**Estimated Duration:** +15-20 minutes + +**Audience:** +React developers, UI/UX engineers, and front-end specialists working with modern React applications. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Edit (as part of GitHub Copilot Chat) +- Access to the SimpleFullStack React project + +## Lab Description + +GitHub Copilot Edit enables you to apply precise modifications to specific files or sections within your React codebase using natural language instructions. This tool excels when you need targeted changes to a well-defined set of components rather than extensive modifications across your entire project. Simply highlight the code you want to change, provide instructions like "add error handling" or "refactor using React hooks," and Copilot rewrites the code for you – while always showing you the diff for review before any changes are saved. + +What makes Edit mode powerful is that you maintain full control. Copilot does the work, but you get the final say. You can also enhance its effectiveness by providing custom instructions that teach Copilot your team's coding standards, style preferences, and documentation requirements. + +## Lab Steps + +### 1. Launch Copilot Edit + +- Open Visual Studio Code with the SimpleFullStack project. +- Open the Copilot Chat interface by clicking the Copilot icon in the activity bar or using the keyboard shortcut `Ctrl+Shift+I` (Windows/Linux) or `Cmd+Shift+I` (Mac). +- In the Copilot Chat window, click the **Copilot Edits** tab. + +### 2. Add a Component to Context + +- Navigate to `samples/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx`. +- Click `+ Add Files` in the Copilot Edit interface and select the `Footer.tsx` file to add it to the edit context. + +### 3. Transform the Empty Footer + +In the Copilot Edit prompt, enter the following natural language instruction: + +```plaintext +Transform this empty footer into a comprehensive modern footer with company links, social media icons, and copyright information. Include navigation sections for Products, Resources, Company, and Legal. Use Material UI components and make it responsive for all screen sizes. +``` + +- Review the proposed changes in the chat interface. +- Click "Apply" if you're satisfied with the edits. + +### 4. Add Theming Support + +- Keep the `Footer.tsx` file in the edit context. +- Enter the following prompt: + +```plaintext +Enhance this footer to support dark mode and light mode themes. Make sure the colors, borders, and backgrounds adapt to the current theme. Extract theme-specific styles into a separate constant or function. +``` + +- Review the proposed changes that add theme support. +- Apply the edits if they meet your requirements. + +### 5. Implement Internationalization + +- Keep the `Footer.tsx` file in the edit context. +- Enter the following prompt: + +```plaintext +Add internationalization support to this footer component. Create a separate file for translations in English and Spanish. Import and use these translations in the footer component. Make sure all text content is translatable. +``` + +- Review the comprehensive changes that implement i18n support. +- Apply the edits to enhance the component. + +### 6. Implement Accessibility Improvements + +- Keep the `Footer.tsx` file in the edit context. +- Enter this prompt: + +```plaintext +Improve the accessibility of this footer by adding proper ARIA attributes, ensuring proper color contrast, and making it fully keyboard navigable. Add screen reader friendly descriptions and ensure the component meets WCAG 2.1 AA standards. +``` + +- Review how Copilot enhances the component with accessibility features. +- Apply the changes to make your footer more accessible. + +### 7. Create a Newsletter Signup Form + +- Keep the `Footer.tsx` file in the edit context. +- Enter this prompt: + +```plaintext +Add a newsletter signup form to the footer with email validation, submission handling, and success/error states. Include proper form validation and error messages. Make sure the form is accessible and responsive. +``` + +- Review how Copilot adds a complete newsletter signup form to the footer. +- Apply the changes to add this new feature. + +### 8. Add Analytics and Performance Tracking + +- Keep the `Footer.tsx` file in the edit context. +- Enter this prompt: + +```plaintext +Add analytics tracking to the footer links and form submissions. Create a custom hook for tracking events and use it in the footer component. Also, optimize the component's performance using React.memo and ensure link clicks are tracked properly. +``` + +- Review how Copilot adds analytics and performance optimizations. +- Apply the changes to complete your enhanced footer. + +## Best Practices for Copilot Edit with React + +- **Be Specific About React Patterns:** Mention specific React patterns like hooks, memoization, or context when requesting changes. +- **Focus on Complete Components:** Edit entire component files rather than fragments for more coherent results. +- **Consider Component Relationships:** Mention parent-child relationships when modifying components that interact. +- **Specify State Management Approach:** Clearly indicate which state management approach you want (Context, Redux, Zustand, etc.). +- **Request TypeScript Types:** When working with TypeScript, explicitly ask for proper type definitions. +- **Prioritize Performance:** Request specific React performance optimizations like memoization, callback optimization, or render optimizations. + +## Summary + +By completing this lab, you've learned to: + +- Use GitHub Copilot Edit to transform a basic component into a comprehensive feature +- Add theme support to make components adapt to light and dark modes +- Implement internationalization for multilingual support +- Enhance accessibility to meet modern web standards +- Add complex features like newsletter signup forms with validation +- Integrate analytics tracking and performance optimizations + +These skills will help you leverage GitHub Copilot Edit to maintain and improve your React codebase efficiently while maintaining full control over the changes. diff --git a/lessons/01-github-copilot-tools/labs/react/01c-exploring-copilot-agent-(react).md b/lessons/01-github-copilot-tools/labs/react/01c-exploring-copilot-agent-(react).md new file mode 100644 index 0000000..77948bc --- /dev/null +++ b/lessons/01-github-copilot-tools/labs/react/01c-exploring-copilot-agent-(react).md @@ -0,0 +1,162 @@ +### Lab: Automating Tasks with GitHub Copilot Agent in React + +## Overview + +**Goal:** +Learn how to leverage GitHub Copilot Agent to autonomously implement complex features and architectural changes across your React application. + +**Estimated Duration:** +25-30 minutes + +**Audience:** +React developers, front-end architects, and full-stack engineers working with React applications. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot Chat with Agent capabilities +- Access to the SimpleFullStack React project + +## Lab Description + +GitHub Copilot Agent represents the most powerful mode in Copilot Chat, enabling autonomous planning and execution across your entire React project. With Agent mode, you provide a high-level prompt and Copilot independently selects the right files, runs necessary tools or terminal commands, and applies code edits until the task is complete. Unlike Edit mode, Agent analyzes related code and identifies additional changes needed across the project to maintain consistency. + +What distinguishes Agent mode is its ability to work autonomously - applying edits automatically rather than waiting for explicit approval at each step, while still surfacing potentially risky commands for review. This creates a continuous-edit "driver" model where you define the goal and Copilot executes updates without interruption. + +For maximum effectiveness, Agent mode works best with custom instructions that define your project structure, coding standards, and other guidelines. These instructions provide a stronger foundation for Copilot to work from, resulting in more consistent and aligned outcomes across multiple sessions. + +## Lab Steps + +### 1. Launch Copilot Agent + +- Open Visual Studio Code with the SimpleFullStack React project. +- Navigate to the Copilot Chat panel by clicking the Copilot icon in the activity bar or using the keyboard shortcut `Ctrl+Shift+I` (Windows/Linux) or `Cmd+Shift+I` (Mac). +- Select the "Agent" tab from the top of the chat interface. + +### 2. Implement a Complete Feature: Dark Mode + +Ask Copilot Agent to implement a complete dark mode feature for your React application with this prompt: + +``` +Implement a dark mode feature for this React application. The feature should: +1. Add a toggle button in the header component +2. Save the user's preference in local storage +3. Update the theme based on the user's preference +4. Ensure the toggle works across the entire application +``` + +Observe as Copilot Agent: + +- Analyzes the existing theme implementation in the project +- Identifies all necessary files to modify +- Makes changes to the theme configuration and components +- Adds the toggle button to the appropriate navigation component +- Implements the local storage persistence + +Review the changes and test the dark mode toggle functionality. + +### 3. Create a New Component System with Testing + +Ask Copilot Agent to create a complete component system with the following prompt: + +``` +Create a notification system for the React application that includes: +1. A toast notification component that can display success, error, warning, and info messages +2. A notification context provider to manage notifications across the app +3. Custom hooks to trigger notifications from any component +4. Unit tests for all the new components +5. Update at least one existing component to demonstrate using the notification system +``` + +Observe as Copilot Agent: + +- Creates multiple new files for the notification system +- Implements the React Context API for state management +- Develops custom hooks for the notification API +- Writes comprehensive unit tests +- Integrates the system with existing components + +### 4. Performance Optimization Across the Application + +Prompt Copilot Agent to perform a comprehensive performance optimization: + +``` +Optimize the performance of this React application by: +1. Identifying and memoizing expensive components +2. Adding virtualization for any long lists in the application +3. Implementing lazy loading for routes and heavy components +4. Adding Suspense boundaries with appropriate fallback UIs +5. Optimizing any unnecessary re-renders +``` + +Watch as Copilot Agent: + +- Analyzes the application for performance bottlenecks +- Applies React.memo to appropriate components +- Implements virtualization for list components +- Sets up code splitting with React.lazy and Suspense +- Optimizes state management to prevent unnecessary re-renders + +### 5. API Integration and Error Handling + +Ask Copilot Agent to improve the API integration with this prompt: + +``` +Enhance the API integration in this React application by: +1. Implementing a comprehensive error handling system for API calls +2. Adding loading states and skeleton loaders during API requests +3. Creating a retry mechanism for failed requests +4. Implementing request caching for improved performance +5. Adding offline support capabilities where appropriate +``` + +Observe as Copilot Agent: + +- Modifies the existing API service layer +- Creates new components for error states and loading indicators +- Implements retry logic for network failures +- Sets up a caching mechanism for API responses +- Adds offline capabilities where appropriate + +### 6. Architectural Refactoring + +Challenge Copilot Agent with a larger architectural change: + +``` +Refactor the state management in this application to use Redux Toolkit. This should include: +1. Setting up the Redux store with proper configuration +2. Creating slices for the main data entities +3. Converting existing state management to use Redux +4. Implementing thunks for asynchronous operations +5. Ensuring type safety throughout the implementation +``` + +Watch as Copilot Agent: + +- Installs the necessary dependencies +- Creates the Redux store structure +- Develops entity slices with reducers and actions +- Converts existing state management to Redux +- Implements async logic with Redux Toolkit + +## Best Practices for Using Copilot Agent with React + +- **Provide Context in Your Prompts:** Mention the React version, state management approach, and other key technologies. +- **Break Large Tasks into Steps:** For complex features, guide Copilot with a numbered list of steps. +- **Specify Coding Standards:** Mention preferences for functional components, hooks usage, and TypeScript requirements. +- **Review Changes Incrementally:** Pause between major feature additions to review and test. +- **Ask for Explanations:** Request that Copilot add comments explaining complex implementations. +- **Guide Architectural Decisions:** Be clear about patterns like container/presentational components or custom hooks. + +## Summary + +By completing this lab, you've learned to: + +- Use GitHub Copilot Agent to implement complete features autonomously +- Create new component systems with proper testing +- Optimize application performance across multiple components +- Enhance API integration with robust error handling +- Refactor application architecture with minimal manual intervention + +These capabilities demonstrate how GitHub Copilot Agent can dramatically accelerate development by handling complex, multi-file changes that would typically require significant manual effort. diff --git a/lessons/01-github-copilot-tools/labs/react/01d-exploring-copilot-inline-(react).md b/lessons/01-github-copilot-tools/labs/react/01d-exploring-copilot-inline-(react).md new file mode 100644 index 0000000..e9b325e --- /dev/null +++ b/lessons/01-github-copilot-tools/labs/react/01d-exploring-copilot-inline-(react).md @@ -0,0 +1,139 @@ +### Lab: Exploring GitHub Copilot Inline with React + +## Overview + +**Goal:** +Learn how to use GitHub Copilot's inline chat and autocomplete features to make quick, incremental changes directly within your React components. You'll practice renaming, generating functions, and enhancing components using inline prompts. + +**Estimated Duration:** +15-20 minutes + +**Audience:** +React developers looking to boost their productivity through fast, context-aware code editing within their IDE. + +**Prerequisites:** + +- Visual Studio Code installed +- GitHub Copilot extension enabled +- Access to GitHub Copilot (with inline chat feature) +- Familiarity with React and TypeScript + +## Lab Steps + +### 1. Open Target File + +- Navigate to the `samples/SimpleFullStack/Web/src/ui/components/global_snackbar` folder in Visual Studio Code. +- Open the `GlobalSnackbar.tsx` file for editing. + +### 2. Rename Component Method + +- Locate the existing `handleClose` method in the `GlobalSnackbar` component. +- Place your cursor on `handleClose`. +- Press `Ctrl+I` (Windows/Linux) or `Cmd+I` (Mac) to open an inline Copilot prompt. +- Enter the following prompt: + + ```plaintext + Rename symbol `handleClose` to `dismissSnackbar` + ``` + + Press Enter. Review the rename suggestions and apply them. + +### 3. Add a Custom Animation Method + +- With the same file still open, place your cursor after the `TransitionUp` function and before the `GlobalSnackbar` component. +- Open a new inline Copilot prompt. +- Enter the following prompt: + + ```plaintext + Add a new transition function for sliding from the left + ``` + + Accept the generated code suggestion, which should look similar to this: + + ```typescript + function TransitionLeft(props: SlideProps) { + return ; + } + ``` + +### 4. Create a Duration Helper + +- Place your cursor inside the `GlobalSnackbar` component, after the destructured hook variables and before the `dismissSnackbar` method. +- In a new inline prompt, enter: + + ```plaintext + Add a function to determine duration based on severity + ``` + + - Accept the method stub and close the prompt. + - Delete the return statement if you want to see Copilot's ghost text suggestions. + - Observe the "ghost text" Copilot suggests in gray text. + - Press `Tab` to accept the suggestion, or arrow keys to navigate through the suggestion. + +### 5. Enhance the Snackbar Styling + +- Locate the `` component in the return statement. +- Place your cursor inside the `sx` prop of the Alert component. +- Open a new inline Copilot prompt (`Ctrl+I`) and enter: + + ```plaintext + Enhance styling with dynamic colors based on severity + ``` + + Accept the Copilot suggestion to add dynamic styling based on the severity level. + +### 6. Add Accessibility Features + +- Locate the `` component in the return statement. +- Place your cursor at the end of the props, before the closing `>`. +- Open an inline prompt and enter: + + ```plaintext + Add accessibility attributes + ``` + + Review and accept Copilot's suggested accessibility enhancements. + +### 7. Generate a New Hook + +- Open a new file called `useSnackbarTimer.ts` in the same directory. +- With your cursor at the beginning of the empty file, open an inline prompt and enter: + + ```plaintext + Create a custom hook that manages snackbar display timing + ``` + + Accept the hook implementation that Copilot suggests, which should include imports, type definitions, and the hook logic. + +### 8. Integrate the Hook + +- Return to `GlobalSnackbar.tsx`. +- Add an import for the new hook at the top of the file. +- Place your cursor inside the component, after the useGlobalSnackbar hook. +- Open an inline prompt and enter: + + ```plaintext + Use the useSnackbarTimer hook + ``` + + Accept the suggestion to integrate the hook into the component. + +## Best Practices + +- **Use Inline for Quick Enhancements:** Inline prompts are perfect for adding small methods, JSX elements, or styling. +- **Leverage TypeScript Hints:** Copilot uses TypeScript types to provide better suggestions, so maintain good type definitions. +- **Chain Small Changes:** Build complex features through a series of small, focused inline prompts. +- **Position Your Cursor Strategically:** Place your cursor where you want the code to be inserted for more accurate context. +- **Review Component Logic:** Always verify that added methods work with your component's state and props. + +## Summary + +By completing this lab, you've learned to: + +- Use GitHub Copilot's inline chat to rename methods and generate new functions in React components. +- Create and implement custom React hooks using inline prompts. +- Enhance component UI with inline-suggested JSX elements and styling. +- Improve accessibility and user experience with minimal effort. +- Accept ghost text to streamline minor edits and logic insertions. + +GitHub Copilot Inline provides an efficient way to iterate rapidly on your React components without breaking your development flow. diff --git a/samples/SimpleFullStack/AzureFunctionC#/.gitignore b/samples/SimpleFullStack/AzureFunctionC#/.gitignore deleted file mode 100644 index ff5b00c..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/.gitignore +++ /dev/null @@ -1,264 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. - -# Azure Functions localsettings file -local.settings.json - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ - -# Visual Studio 2015 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# DNX -project.lock.json -project.fragment.lock.json -artifacts/ - -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.pch -*.pdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings -# but database connection strings (with potential passwords) will be unencrypted -#*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/packages/* -# except build/, which is used as an MSBuild target. -!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignoreable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -node_modules/ -orleans.codegen.cs - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm - -# SQL Server files -*.mdf -*.ldf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc \ No newline at end of file diff --git a/samples/SimpleFullStack/AzureFunctionC#/Model/Order.cs b/samples/SimpleFullStack/AzureFunctionC#/Model/Order.cs deleted file mode 100644 index 3ccd5bc..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/Model/Order.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Text.Json.Serialization; -using Newtonsoft.Json; - - -namespace api.Model -{ - public class Order - { - - [JsonPropertyName("orderId")] - [JsonProperty("orderId")] - public required string OrderId { get; set; } - - [JsonPropertyName("companyId")] - [JsonProperty("companyId")] - public required string CompanyId { get; set; } - - [JsonPropertyName("customerName")] - [JsonProperty("customerName")] - public required string CustomerName { get; set; } - - [JsonPropertyName("orderDate")] - [JsonProperty("orderDate")] - public DateTime OrderDate { get; set; } - - [JsonPropertyName("totalAmount")] - [JsonProperty("totalAmount")] - public double TotalAmount { get; set; } - } -} \ No newline at end of file diff --git a/samples/SimpleFullStack/AzureFunctionC#/Model/OrderResponse.cs b/samples/SimpleFullStack/AzureFunctionC#/Model/OrderResponse.cs deleted file mode 100644 index 0457711..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/Model/OrderResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Text.Json.Serialization; -using Newtonsoft.Json; -using api.Model; - - -namespace api.Model -{ - public class OrderResponse - { - [JsonPropertyName("orders")] - [JsonProperty("orders")] - public List Orders { get; set; } - - [JsonPropertyName("totalAmount")] - [JsonProperty("totalAmount")] - public double TotalAmount { get; set; } - - } -} \ No newline at end of file diff --git a/samples/SimpleFullStack/AzureFunctionC#/OrderFunctions.cs b/samples/SimpleFullStack/AzureFunctionC#/OrderFunctions.cs deleted file mode 100644 index b2c9299..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/OrderFunctions.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Text.Json; -using System.Threading.Tasks; -using Microsoft.Azure.Functions.Worker; -using Microsoft.Azure.Functions.Worker.Http; -using Microsoft.Extensions.Logging; -using api.Model; -using api.Services; -using System.Web; // For HttpUtility - -namespace api -{ - public class OrderFunctions - { - private readonly ILogger _logger; - private readonly IOrdersService _ordersService; - - public OrderFunctions(ILoggerFactory loggerFactory, IOrdersService ordersService) - { - // Corrected logger type to OrderFunctions. - _logger = loggerFactory.CreateLogger(); - _ordersService = ordersService; - } - - [Function("GetOrders")] - public async Task Run( - [HttpTrigger(AuthorizationLevel.Function, "get", Route = "orders")] HttpRequestData req) - { - _logger.LogInformation("Processing request to fetch orders."); - - try - { - // Fetch orders from the JSON file using the service. - var orders = _ordersService.FetchOrders(); - - var response = req.CreateResponse(HttpStatusCode.OK); - await response.WriteAsJsonAsync(orders); - return response; - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred while processing the request."); - var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError); - await errorResponse.WriteStringAsync($"Error fetching products: {ex.Message}"); - return errorResponse; - } - } - - [Function("GetOrdersByCompany")] - public async Task GetOrdersByCompany( - [HttpTrigger(AuthorizationLevel.Function, "get", Route = "orders/company")] HttpRequestData req) - { - _logger.LogInformation("Processing request to fetch orders by company."); - - try - { - // Parse the query parameter for companyId. - var query = HttpUtility.ParseQueryString(req.Url.Query); - var companyId = query["companyId"]; - - if (string.IsNullOrEmpty(companyId)) - { - var badRequestResponse = req.CreateResponse(HttpStatusCode.BadRequest); - await badRequestResponse.WriteStringAsync("Missing or invalid 'companyId' query parameter."); - return badRequestResponse; - } - - // Fetch orders from the service. - var orders = _ordersService.FetchOrders(); - - // Filter orders by the provided companyId. - var filteredOrders = _ordersService.FilterByCompany(companyId, orders); - - //call the get toal function from the Order Service - double totalAmount = _ordersService.CalculateTotalAmount(filteredOrders); - - - var orderResponse = new OrderResponse - { - Orders = filteredOrders, - TotalAmount = totalAmount - }; - - var response = req.CreateResponse(HttpStatusCode.OK); - await response.WriteAsJsonAsync(orderResponse); - return response; - } - catch (Exception ex) - { - _logger.LogError(ex, "An error occurred while processing the request."); - var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError); - await errorResponse.WriteStringAsync($"Error fetching orders by company: {ex.Message}"); - return errorResponse; - } - } - } -} diff --git a/samples/SimpleFullStack/AzureFunctionC#/Program.cs b/samples/SimpleFullStack/AzureFunctionC#/Program.cs deleted file mode 100644 index 3937b72..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/Program.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.Azure.Functions.Worker.Builder; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.DependencyInjection; -using api.Services; -using api.Model; - -// Ensure you include the namespace where IOrdersService and OrdersService are defined. -// using YourNamespace.Services; - -var builder = FunctionsApplication.CreateBuilder(args); - -builder.ConfigureFunctionsWebApplication(); - -// Register your custom orders service. -builder.Services.AddSingleton(); - -// Application Insights isn't enabled by default. See https://aka.ms/AAt8mw4. -// builder.Services -// .AddApplicationInsightsTelemetryWorkerService() -// .ConfigureFunctionsApplicationInsights(); - -builder.Build().Run(); \ No newline at end of file diff --git a/samples/SimpleFullStack/AzureFunctionC#/Properties/launchSettings.json b/samples/SimpleFullStack/AzureFunctionC#/Properties/launchSettings.json deleted file mode 100644 index 53a4da1..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/Properties/launchSettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "profiles": { - "api": { - "commandName": "Project", - "commandLineArgs": "--port 7137", - "launchBrowser": false - } - } -} \ No newline at end of file diff --git a/samples/SimpleFullStack/AzureFunctionC#/Service/OrdersService.cs b/samples/SimpleFullStack/AzureFunctionC#/Service/OrdersService.cs deleted file mode 100644 index 4d59cb4..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/Service/OrdersService.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.Json; -using Microsoft.Extensions.Logging; -using api.Model; - - -namespace api.Services -{ - - - public interface IOrdersService - { - List FetchOrders(); - List FilterByCompany(string companyId, List orders); - double CalculateTotalAmount(List orders); - } - - public class OrdersService : IOrdersService - { - private readonly string _ordersFilePath; - - public OrdersService() - { - // Use AppContext.BaseDirectory to point to the output folder. - _ordersFilePath = Path.Combine(AppContext.BaseDirectory, "orders.json"); - } - - - public List FetchOrders() - { - if (!File.Exists(_ordersFilePath)) - { - return new List(); - } - - string jsonFromFile = File.ReadAllText(_ordersFilePath); - var orders = JsonSerializer.Deserialize>(jsonFromFile); - - if (orders == null) - { - return new List(); - } - return orders; - } - - // Function to filter by company ID. - public List FilterByCompany(string companyId, List orders) - { - return orders.FindAll(o => o.CompanyId == companyId); - } - - // Calculate the total amount of orders. - - public double CalculateTotalAmount(List orders) - { - if (orders == null || orders.Count == 0) - { - return 0; - } - - double totalAmount = 0; - foreach (var order in orders) - { - totalAmount += order.TotalAmount; - } - - return totalAmount; - } - } -} diff --git a/samples/SimpleFullStack/AzureFunctionC#/Service/SecondOrdersService.cs b/samples/SimpleFullStack/AzureFunctionC#/Service/SecondOrdersService.cs deleted file mode 100644 index e69de29..0000000 diff --git a/samples/SimpleFullStack/AzureFunctionC#/api.csproj b/samples/SimpleFullStack/AzureFunctionC#/api.csproj deleted file mode 100644 index 6dc9309..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/api.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - net8.0 - v4 - Exe - enable - enable - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - Never - - - - - - \ No newline at end of file diff --git a/samples/SimpleFullStack/AzureFunctionC#/host.json b/samples/SimpleFullStack/AzureFunctionC#/host.json deleted file mode 100644 index ee5cf5f..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/host.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "2.0", - "logging": { - "applicationInsights": { - "samplingSettings": { - "isEnabled": true, - "excludedTypes": "Request" - }, - "enableLiveMetricsFilters": true - } - } -} \ No newline at end of file diff --git a/samples/SimpleFullStack/AzureFunctionC#/orders.json b/samples/SimpleFullStack/AzureFunctionC#/orders.json deleted file mode 100644 index 6dbe7e5..0000000 --- a/samples/SimpleFullStack/AzureFunctionC#/orders.json +++ /dev/null @@ -1,72 +0,0 @@ -[ - { - "orderId": "1", - "companyId": "1", - "customerName": "Alice Johnson", - "orderDate": "2025-03-28T09:00:00Z", - "totalAmount": 100.0 - }, - { - "orderId": "2", - "companyId": "1", - "customerName": "Bob Smith", - "orderDate": "2025-03-27T14:30:00Z", - "totalAmount": 150.5 - }, - { - "orderId": "3", - "companyId": "1", - "customerName": "Carol Williams", - "orderDate": "2025-03-26T10:15:00Z", - "totalAmount": 300.0 - }, - { - "orderId": "4", - "companyId": "2", - "customerName": "David Brown", - "orderDate": "2025-03-25T16:45:00Z", - "totalAmount": 200.75 - }, - { - "orderId": "5", - "companyId": "2", - "customerName": "Eve Davis", - "orderDate": "2025-03-24T12:00:00Z", - "totalAmount": 175.0 - }, - { - "orderId": "6", - "companyId": "2", - "customerName": "Frank Miller", - "orderDate": "2025-03-23T08:30:00Z", - "totalAmount": 225.0 - }, - { - "orderId": "7", - "companyId": "3", - "customerName": "Grace Wilson", - "orderDate": "2025-03-22T11:00:00Z", - "totalAmount": 80.0 - }, - { - "orderId": "8", - "companyId": "4", - "customerName": "Hank Moore", - "orderDate": "2025-03-21T13:15:00Z", - "totalAmount": 90.25 - }, - { - "orderId": "9", - "companyId": "5", - "customerName": "Ivy Taylor", - "orderDate": "2025-03-20T15:30:00Z", - "totalAmount": 300.99 - }, - { - "orderId": "10", - "companyId": "6", - "customerName": "Jack Anderson", - "orderDate": "2025-03-19T17:45:00Z", - "totalAmount": 50.0 - } -] \ No newline at end of file diff --git a/samples/SimpleFullStack/React/.env.example b/samples/SimpleFullStack/React/.env.example deleted file mode 100644 index 23c2e6d..0000000 --- a/samples/SimpleFullStack/React/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -VITE_API_BASE_URL=http://localhost:7071 - - - diff --git a/samples/SimpleFullStack/React/.eslintrc.cjs b/samples/SimpleFullStack/React/.eslintrc.cjs deleted file mode 100644 index 6e8698b..0000000 --- a/samples/SimpleFullStack/React/.eslintrc.cjs +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:react-hooks/recommended", - ], - ignorePatterns: ["dist", ".eslintrc.cjs"], - parser: "@typescript-eslint/parser", - plugins: ["react-refresh"], - rules: { - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - }, -}; diff --git a/samples/SimpleFullStack/React/.gitignore b/samples/SimpleFullStack/React/.gitignore deleted file mode 100644 index 374e40a..0000000 --- a/samples/SimpleFullStack/React/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? - - -local.env -.env.local -.env -.env.uat -/package-lock.json -package-lock.json diff --git a/samples/SimpleFullStack/React/.prettierignore b/samples/SimpleFullStack/React/.prettierignore deleted file mode 100644 index 85191cc..0000000 --- a/samples/SimpleFullStack/React/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -build/ -node_modules/ -package-lock.json \ No newline at end of file diff --git a/samples/SimpleFullStack/React/.prettierrc b/samples/SimpleFullStack/React/.prettierrc deleted file mode 100644 index 56ee258..0000000 --- a/samples/SimpleFullStack/React/.prettierrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "semi": true, - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "all", - "bracketSpacing": true, - "arrowParens": "avoid", - "endOfLine": "auto" -} \ No newline at end of file diff --git a/samples/SimpleFullStack/React/README.md b/samples/SimpleFullStack/React/README.md deleted file mode 100644 index 5c4c0ff..0000000 --- a/samples/SimpleFullStack/React/README.md +++ /dev/null @@ -1,4 +0,0 @@ -npm run dev - -.env -VITE_API_BASE_URL=http://localhost:7071 diff --git a/samples/SimpleFullStack/React/copilot-instructions.md b/samples/SimpleFullStack/React/copilot-instructions.md deleted file mode 100644 index a67f054..0000000 --- a/samples/SimpleFullStack/React/copilot-instructions.md +++ /dev/null @@ -1,30 +0,0 @@ -# Copilot Instructions for React Frontend - -## Project Overview -This project is a frontend application built with React and TypeScript. It interacts with backend APIs and provides a user interface for the application. - -## Using GitHub Copilot Effectively -- Use Copilot to generate React components, hooks, and utility functions. -- Ask Copilot to write unit and integration tests for components. -- Use Copilot to generate TypeScript interfaces and types. - -## Coding Conventions -- Use PascalCase for component names and camelCase for variables/functions. -- Prefer functional components and React hooks. -- Keep components small and focused. -- Use TypeScript for type safety. - -## Example Copilot Prompts -- "Create a React component for displaying a list of products." -- "Write a custom hook for fetching data from the API." -- "Generate a TypeScript interface for the Order object." -- "Write a unit test for the ProductList component using React Testing Library." - -## Running and Testing -- Install dependencies: `npm install` -- Run: `npm start` -- Test: `npm test` - -## Additional Tips -- Use Copilot Chat for code explanations and refactoring suggestions. -- Use inline Copilot suggestions for quick code completions. diff --git a/samples/SimpleFullStack/React/index.html b/samples/SimpleFullStack/React/index.html deleted file mode 100644 index 78db3e1..0000000 --- a/samples/SimpleFullStack/React/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - Example - - - -
- - - diff --git a/samples/SimpleFullStack/React/package.json b/samples/SimpleFullStack/React/package.json deleted file mode 100644 index da2783d..0000000 --- a/samples/SimpleFullStack/React/package.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "name": "frontend", - "private": true, - "version": "0.0.0", - "type": "module", - "engines": { - "node": ">18.1" - }, - "scripts": { - "test": "vitest", - "dev": "vite --open", - "start:uat": "env-cmd -f .env.uat vite --open", - "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "prettier": "npx prettier --write .", - "visualize": "npx vite-bundle-visualizer --open --port 5000 --output dist/stats.html" - }, - "dependencies": { - "@azure/msal-browser": "^4.0.2", - "@azure/msal-react": "^3.0.2", - "@emotion/react": "^11.11.4", - "@emotion/styled": "^11.11.5", - "@hookform/resolvers": "^3.9.0", - "@mui/icons-material": "^5.15.15", - "@mui/material": "^5.15.15", - "@mui/x-data-grid": "^6.20.0", - "@mui/x-date-pickers": "^7.2.0", - "@tanstack/react-query": "^5.39.0", - "axios": "^1.7.1", - "dayjs": "^1.11.13", - "moment": "^2.30.1", - "npm": "^10.8.3", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "react-hook-form": "^7.51.3", - "react-router-dom": "^6.22.3", - "zod": "^3.23.8", - "zustand": "^4.5.2" - }, - "devDependencies": { - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.2.0", - "@testing-library/user-event": "^14.6.1", - "@types/jest": "^29.5.14", - "@types/node": "^20.12.12", - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22", - "@typescript-eslint/eslint-plugin": "^7.2.0", - "@typescript-eslint/parser": "^7.2.0", - "@vitejs/plugin-react": "^4.2.1", - "depcheck": "^1.4.7", - "env-cmd": "^10.1.0", - "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", - "jsdom": "^26.0.0", - "typescript": "^5.4.5", - "vite": "^5.2.0", - "vitest": "^3.0.9" - } -} \ No newline at end of file diff --git a/samples/SimpleFullStack/React/public/Neudesic-N.svg b/samples/SimpleFullStack/React/public/Neudesic-N.svg deleted file mode 100644 index 00fb595..0000000 --- a/samples/SimpleFullStack/React/public/Neudesic-N.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/samples/SimpleFullStack/React/public/Neudesic_Logo.svg b/samples/SimpleFullStack/React/public/Neudesic_Logo.svg deleted file mode 100644 index 59a0237..0000000 --- a/samples/SimpleFullStack/React/public/Neudesic_Logo.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/SimpleFullStack/React/public/ProSpec_AI.svg b/samples/SimpleFullStack/React/public/ProSpec_AI.svg deleted file mode 100644 index 0aad809..0000000 --- a/samples/SimpleFullStack/React/public/ProSpec_AI.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/samples/SimpleFullStack/React/public/ProSpec_AI_Chat.svg b/samples/SimpleFullStack/React/public/ProSpec_AI_Chat.svg deleted file mode 100644 index 5c0619a..0000000 --- a/samples/SimpleFullStack/React/public/ProSpec_AI_Chat.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/samples/SimpleFullStack/React/src/App.tsx b/samples/SimpleFullStack/React/src/App.tsx deleted file mode 100644 index c3cfc16..0000000 --- a/samples/SimpleFullStack/React/src/App.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { ThemeProvider } from '@emotion/react'; -import { CssBaseline } from '@mui/material'; -import { Suspense } from 'react'; -import GlobalModal from 'components/globals/GlobalModal'; -import GlobalSnackbar from 'components/globals/GlobalSnackbar'; -import { createBrowserRouter, RouterProvider } from 'react-router-dom'; -import { LocalizationProvider } from '@mui/x-date-pickers'; -import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import './global.css'; -import GlobalSidePanel from 'components/globals/GlobalSidePanel'; -import GlobalSecondaryModal from 'components/globals/GlobalSecondaryModal'; - -import { myRoutes } from './routes/privateRoutes'; -import { theme } from './styles/themes'; - - -function App() { - - return ( - - - Global Loading...}> - - - - - - - - - - ); -} - -export default App; diff --git a/samples/SimpleFullStack/React/src/assets/Neudesic-N.svg b/samples/SimpleFullStack/React/src/assets/Neudesic-N.svg deleted file mode 100644 index 00fb595..0000000 --- a/samples/SimpleFullStack/React/src/assets/Neudesic-N.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/samples/SimpleFullStack/React/src/assets/Neudesic_Logo.svg b/samples/SimpleFullStack/React/src/assets/Neudesic_Logo.svg deleted file mode 100644 index 59a0237..0000000 --- a/samples/SimpleFullStack/React/src/assets/Neudesic_Logo.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/samples/SimpleFullStack/React/src/assets/neudesic-logo-black.svg b/samples/SimpleFullStack/React/src/assets/neudesic-logo-black.svg deleted file mode 100644 index 34e47fb..0000000 --- a/samples/SimpleFullStack/React/src/assets/neudesic-logo-black.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/assets/neudesic-n-logo.svg b/samples/SimpleFullStack/React/src/assets/neudesic-n-logo.svg deleted file mode 100644 index 228d961..0000000 --- a/samples/SimpleFullStack/React/src/assets/neudesic-n-logo.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/samples/SimpleFullStack/React/src/components/FilterComponent.tsx b/samples/SimpleFullStack/React/src/components/FilterComponent.tsx deleted file mode 100644 index 1921f95..0000000 --- a/samples/SimpleFullStack/React/src/components/FilterComponent.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { - Box, - FormControl, - InputLabel, - MenuItem, - Select, - TextField, -} from "@mui/material"; - -export type Filter = { - selected: string | undefined; - options: Array | undefined; - onChange: (selected: string) => void; -}; - -type FilterComponentProps = { - search: string | undefined; - onSearch: (search: string) => void; - dropdownData?: Array>; -}; - -type Base = { - id: string; - name: string; -}; - -const FilterComponent = ({ - search, - onSearch, - dropdownData, -}: FilterComponentProps) => { - return ( - - onSearch(e.target.value)} - label="Search" - variant="outlined" - sx={{ - flexGrow: 2, - }} - /> - - {dropdownData && - dropdownData.map(({ selected, options, onChange }, index) => ( - - Category - - - ))} - - ); -}; - -export default FilterComponent; diff --git a/samples/SimpleFullStack/React/src/components/TabsPanels.tsx b/samples/SimpleFullStack/React/src/components/TabsPanels.tsx deleted file mode 100644 index 0c22e07..0000000 --- a/samples/SimpleFullStack/React/src/components/TabsPanels.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { Box, Tab, Tabs } from "@mui/material"; - -export type Tabs = { - id: string; - title: string; - conent: React.ReactNode; -}; - -const TabsPanels = ({ - currentTab, - setCurrentTab, - tabs, -}: { - currentTab: number; - setCurrentTab: (tab: number) => void; - tabs: Tabs[]; -}) => { - const handleChange = (event: React.SyntheticEvent, newValue: number) => { - setCurrentTab(newValue); - }; - - return ( - - - - {tabs.map((tab, index) => ( - - ))} - - - {tabs.map((tab, index) => ( - - {tab.conent} - - ))} - - ); -}; - -export default TabsPanels; - -function a11yProps(id: number) { - return { - id: `simple-tab-${id}`, - "aria-controls": `simple-tabpanel-${id}`, - }; -} - -interface TabPanelProps { - children?: React.ReactNode; - index: number; - value: number; -} - -function CustomTabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - return ( - - ); -} diff --git a/samples/SimpleFullStack/React/src/components/generics/GenericDataGrid.tsx b/samples/SimpleFullStack/React/src/components/generics/GenericDataGrid.tsx deleted file mode 100644 index 98d1083..0000000 --- a/samples/SimpleFullStack/React/src/components/generics/GenericDataGrid.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import Box from '@mui/material/Box'; -import { - DataGrid, - GridColDef, - GridRowParams, - GridValidRowModel, - MuiEvent, -} from '@mui/x-data-grid'; - -export type TypeSafeColDef = GridColDef & { field: keyof T }; - -type GenericDataGridProps = { - rows: T[] | undefined; - columns: GridColDef[]; - onRowClick: ( - params: GridRowParams, - _event: MuiEvent>, - ) => void; - loading?: boolean; - rowCount?: number; - paginationModel?: { - page: number; - pageSize: number; - }; - onPaginationModelChange?: React.Dispatch< - React.SetStateAction<{ - page: number; - pageSize: number; - }> - >; - getRowIdFxn?: (row: T) => string; -}; -const GenericDataGrid = ({ - rows, - columns, - onRowClick, - loading = false, - rowCount, - paginationModel, - onPaginationModelChange, - getRowIdFxn = undefined, -}: GenericDataGridProps) => { - return ( - - 'auto'} - columns={columns} - pageSizeOptions={[25]} - onRowClick={onRowClick} - paginationMode="server" - paginationModel={paginationModel} - onPaginationModelChange={onPaginationModelChange} - slots={{ - noRowsOverlay: () => ( - - No Data - - ), - noResultsOverlay: () => ( - - No Data - - ), - }} - sx={{ - p: 0, - borderRadius: '4px', - backgroundColor: "common.white", - color: 'custom.darkBlue', - '& .MuiDataGrid-columnHeaderTitle': { - fontWeight: '600', - }, - '& .MuiTablePagination-actions > .MuiIconButton-root:not(.Mui-disabled) > svg': - { - fill: 'black', - }, - }} - /> - - ); -}; - -export default GenericDataGrid; diff --git a/samples/SimpleFullStack/React/src/components/generics/GenericForm.tsx b/samples/SimpleFullStack/React/src/components/generics/GenericForm.tsx deleted file mode 100644 index 5ede85e..0000000 --- a/samples/SimpleFullStack/React/src/components/generics/GenericForm.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { - Control, - FieldValues, - FormState, - useForm, - UseFormProps, -} from "react-hook-form"; - -const GenericFrom = < - TFieldValues extends FieldValues = FieldValues, - TContext = any, ->({ - formId, - props, - onSubmitted, - createForm, -}: { - formId: string; - props: UseFormProps; - onSubmitted?: (data: TFieldValues | undefined) => void; - createForm: ( - control: Control, - formState: FormState, - ) => React.ReactNode; -}) => { - const { handleSubmit, control, formState } = useForm(props); - - const onSubmit = handleSubmit((data) => { - if (onSubmitted) { - onSubmitted(data); - } else { - //default logic - } - }); - - return ( -
- {createForm(control, formState)} -
- ); -}; - -export default GenericFrom; diff --git a/samples/SimpleFullStack/React/src/components/generics/GenericTabs.tsx b/samples/SimpleFullStack/React/src/components/generics/GenericTabs.tsx deleted file mode 100644 index f7c574d..0000000 --- a/samples/SimpleFullStack/React/src/components/generics/GenericTabs.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Box, Tab, Tabs } from "@mui/material"; -import { useState } from "react"; - -export type Tabs = { - id: string; - title: string; - conent: React.ReactNode; -}; - -const GenericTabs = ({ tabs }: { tabs: Tabs[] }) => { - const [value, setValue] = useState(0); - - const handleChange = (event: React.SyntheticEvent, newValue: number) => { - setValue(newValue); - }; - - return ( - - - - {tabs.map((tab, index) => ( - - ))} - - - {tabs.map((tab, index) => ( - - {tab.conent} - - ))} - - ); -}; - -export default GenericTabs; - -function a11yProps(id: number) { - return { - id: `simple-tab-${id}`, - "aria-controls": `simple-tabpanel-${id}`, - }; -} - -interface TabPanelProps { - children?: React.ReactNode; - index: number; - value: number; -} - -function CustomTabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - return ( - - ); -} diff --git a/samples/SimpleFullStack/React/src/components/globals/GlobalModal.tsx b/samples/SimpleFullStack/React/src/components/globals/GlobalModal.tsx deleted file mode 100644 index bcb96ed..0000000 --- a/samples/SimpleFullStack/React/src/components/globals/GlobalModal.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import CloseIcon from '@mui/icons-material/Close'; - -import useGlobalModal from 'store/global/useGlobalModal'; -import useGlobalSecondaryModal from 'store/global/useGlobalSecondaryModal'; -import { Typography } from '@mui/material'; -import { useFormConfig } from 'store/useFormConfigs'; - -const GlobalModal = () => { - const { state, closeGlobalModal } = useGlobalModal(); - const { openSecondaryModal, closeSecondaryModal } = useGlobalSecondaryModal(); - const { content, title, titleColor, primaryAction, primaryActionText, closeAction, dialogProps, formName, additionalActions, isDirtyCheck } = state; - const { isDirty } = useFormConfig(); - - const handleClose = () => { - if (closeAction) { - closeAction(); - return; - } - - if (isDirtyCheck && isDirty) { - return openSecondaryModal({ - content: ( - - You have unsaved changes. - Are you sure you want to discard all changes? - - ), - title: 'Discard Changes', - primaryAction: () => { - closeSecondaryModal(); - closeGlobalModal(); - }, - primaryActionText: 'Discard Changes', - closeAction: closeSecondaryModal, - dialogProps: { - open: true, - fullWidth: true, - maxWidth: 'sm', - }, - }); - } - - closeGlobalModal(); - }; - - return ( - - - {title} - - - - - - {content} - - - - - - {additionalActions && additionalActions} - - - - {formName && ( - - )} - - {primaryAction && ( - - )} - - - - - ); -}; - -export default GlobalModal; diff --git a/samples/SimpleFullStack/React/src/components/globals/GlobalSecondaryModal.tsx b/samples/SimpleFullStack/React/src/components/globals/GlobalSecondaryModal.tsx deleted file mode 100644 index 94aa3b3..0000000 --- a/samples/SimpleFullStack/React/src/components/globals/GlobalSecondaryModal.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import Box from '@mui/material/Box'; -import Button from '@mui/material/Button'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogTitle from '@mui/material/DialogTitle'; -import IconButton from '@mui/material/IconButton'; -import CloseIcon from '@mui/icons-material/Close'; -import useGlobalSecondaryModal from 'store/global/useGlobalSecondaryModal'; - - -const GlobalSecondaryModal = () => { - const { globalSecondaryState, closeSecondaryModal } = useGlobalSecondaryModal(); - const { content, title, titleColor, primaryAction, primaryActionText, closeAction, dialogProps, formName } = globalSecondaryState; - - const handleClose = () => { - if (closeAction) { - closeAction(); - } else { - closeSecondaryModal(); - } - }; - - return ( - - - {title} - - - - - - {content} - - - - - - - {formName && ( - - )} - - {primaryAction && ( - - )} - - - - - ); -}; - -export default GlobalSecondaryModal; diff --git a/samples/SimpleFullStack/React/src/components/globals/GlobalSidePanel.tsx b/samples/SimpleFullStack/React/src/components/globals/GlobalSidePanel.tsx deleted file mode 100644 index 880ffaa..0000000 --- a/samples/SimpleFullStack/React/src/components/globals/GlobalSidePanel.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { AppBar, Drawer, Typography } from '@mui/material'; -import useGlobalSidePanel from 'store/global/useGlobalSidePanel'; -import CloseIcon from '@mui/icons-material/Close'; - -const SidePanel = () => { - const { isOpen, content, title, close } = useGlobalSidePanel(); - - if (!isOpen) return null; - - if (!content) return null; - - return ( - - - - - {title} - - - {content} - - ); -} - -export default SidePanel; - - diff --git a/samples/SimpleFullStack/React/src/components/globals/GlobalSnackbar.tsx b/samples/SimpleFullStack/React/src/components/globals/GlobalSnackbar.tsx deleted file mode 100644 index 92e2924..0000000 --- a/samples/SimpleFullStack/React/src/components/globals/GlobalSnackbar.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { - Snackbar, - Alert, - Slide, - SlideProps, - SnackbarOrigin, -} from '@mui/material'; -import useSnackbar from '../../store/global/useGlobalSnackbar'; - -function TransitionUp(props: JSX.IntrinsicAttributes & SlideProps) { - return ; -} - -export default function GlobalSnackbar() { - const { isOpen, message, severity, vertical, horizontal, close } = - useSnackbar(); - - const handleClose = () => { - close(); - }; - - return ( - - - {message} - - - ); -} diff --git a/samples/SimpleFullStack/React/src/components/layouts/PageContainer.tsx b/samples/SimpleFullStack/React/src/components/layouts/PageContainer.tsx deleted file mode 100644 index 0bc42cb..0000000 --- a/samples/SimpleFullStack/React/src/components/layouts/PageContainer.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { Outlet } from 'react-router-dom'; -import LogoutIcon from '@mui/icons-material/Logout'; -import AppBar from '@mui/material/AppBar'; -import Box from '@mui/material/Box'; -import Grid from '@mui/material/Grid'; -import IconButton from '@mui/material/IconButton'; -import Toolbar from '@mui/material/Toolbar'; -import Typography from '@mui/material/Typography'; -import NeudesicLogo from 'assets/Neudesic_Logo.svg'; - -export const PageContainer = () => { - const handleLogout = () => { - }; - - return ( - - - - React Base - } /> - - -
- - - -
- - - © 2024 Neudesic, LLC, All Rights Reserved - - Neudesic Logo - -
- ); -}; diff --git a/samples/SimpleFullStack/React/src/constants/companyDropdownOptions.tsx b/samples/SimpleFullStack/React/src/constants/companyDropdownOptions.tsx deleted file mode 100644 index 51f9645..0000000 --- a/samples/SimpleFullStack/React/src/constants/companyDropdownOptions.tsx +++ /dev/null @@ -1,28 +0,0 @@ -export const verticalOptions = [ - { value: "", label: "Select an option" }, - { value: "unknown", label: "Unknown" }, - { value: "artAndEntertainment", label: "Art And Entertainment" }, - { value: "automotiveAndVehicles", label: "Automotive And Vehicles" }, - { value: "businessAndIndustrial", label: "Business And Industrial" }, - { value: "careers", label: "Careers" }, - { value: "education", label: "Education" }, - { value: "familyAndParenting", label: "Family And Parenting" }, - { value: "finance", label: "Finance" }, - { value: "foodAndDrink", label: "Food And Drink" }, - { value: "healthAndFitness", label: "Health And Fitness" }, - { value: "hobbiesAndInterests", label: "Hobbies And Interests" }, - { value: "homeAndGarden", label: "Home And Garden" }, - { value: "lawGovtAndPolitics", label: "Law, Govt And Politics" }, - { value: "news", label: "News" }, - { value: "pets", label: "Pets" }, - { value: "realEstate", label: "Real Estate" }, - { value: "religionAndSpirituality", label: "Religion And Spirituality" }, - { value: "science", label: "Science" }, - { value: "shopping", label: "Shopping" }, - { value: "society", label: "Society" }, - { value: "sports", label: "Sports" }, - { value: "styleAndFashion", label: "Style And Fashion" }, - { value: "technologyAndComputing", label: "Technology And Computing" }, - { value: "travel", label: "Travel" }, - { value: "adult", label: "Adult" }, -]; \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/constants/mockCompanies.ts b/samples/SimpleFullStack/React/src/constants/mockCompanies.ts deleted file mode 100644 index 74ef705..0000000 --- a/samples/SimpleFullStack/React/src/constants/mockCompanies.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Company } from 'types/company'; - -export const mockCompanies: Company[] = [ - { - id: '1', - tpid: 1001, - accountName: 'Acme Corp', - segment: 'Enterprise', - industry: 'Technology', - subSegment: 'Software', - vertical: 'technologyAndComputing', - }, - { - id: '2', - tpid: 1002, - accountName: 'Beta Solutions', - segment: 'Mid-Market', - industry: 'Healthcare', - subSegment: 'Medical Devices', - vertical: 'healthAndFitness', - }, - { - id: '3', - tpid: 1003, - accountName: 'Gamma Innovations', - segment: 'Small Business', - industry: 'Finance', - subSegment: 'Banking', - vertical: 'finance', - }, - { - id: '4', - tpid: 1004, - accountName: 'Delta Enterprises', - segment: 'Enterprise', - industry: 'Retail', - subSegment: 'E-commerce', - vertical: 'shopping', - }, - { - id: '5', - tpid: 1005, - accountName: 'Epsilon Technologies', - segment: 'Mid-Market', - industry: 'Energy', - subSegment: 'Renewable Energy', - vertical: 'science', - }, - { - id: '6', - tpid: 1006, - accountName: 'Zeta Networks', - segment: 'Enterprise', - industry: 'Telecommunications', - subSegment: 'Internet Service Providers', - vertical: 'technologyAndComputing', - }, - { - id: '7', - tpid: 1007, - accountName: 'Eta Manufacturing', - segment: 'Mid-Market', - industry: 'Manufacturing', - subSegment: 'Automotive', - vertical: 'automotiveAndVehicles', - }, - { - id: '8', - tpid: 1008, - accountName: 'Theta Logistics', - segment: 'Small Business', - industry: 'Transportation', - subSegment: 'Freight', - vertical: 'businessAndIndustrial', - }, - { - id: '9', - tpid: 1009, - accountName: 'Iota Finance', - segment: 'Enterprise', - industry: 'Finance', - subSegment: 'Investment Banking', - vertical: 'finance', - }, - { - id: '10', - tpid: 1010, - accountName: 'Kappa Security', - segment: 'Mid-Market', - industry: 'Cybersecurity', - subSegment: 'Network Security', - vertical: 'technologyAndComputing', - }, -]; diff --git a/samples/SimpleFullStack/React/src/forms/controllers/ControllerDatePicker.tsx b/samples/SimpleFullStack/React/src/forms/controllers/ControllerDatePicker.tsx deleted file mode 100644 index 5c9e8e7..0000000 --- a/samples/SimpleFullStack/React/src/forms/controllers/ControllerDatePicker.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { DatePicker } from "@mui/x-date-pickers"; -import { Control, Controller, FieldValues } from "react-hook-form"; -import dayjs from "dayjs"; - -const ControllerDatePricker = ({ - fieldName, - label, - control, -}: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fieldName: any; - label: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - control: Control; -}) => { - return ( - ( - { - field.onChange(date?.format("YYYY-MM-DD")); - }} - /> - )} - /> - ); -}; - -export default ControllerDatePricker; diff --git a/samples/SimpleFullStack/React/src/forms/controllers/ControllerRadioGroup.tsx b/samples/SimpleFullStack/React/src/forms/controllers/ControllerRadioGroup.tsx deleted file mode 100644 index 5454c8d..0000000 --- a/samples/SimpleFullStack/React/src/forms/controllers/ControllerRadioGroup.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { - Box, - FormControl, - FormControlLabel, - FormHelperText, - FormLabel, - Radio, - RadioGroup, -} from "@mui/material"; -import { - Control, - Controller, - FieldErrors, - FieldValues, - Path, -} from "react-hook-form"; -import { OptionType } from "../../types/helperTypes"; - -const ControllerRadioGroup = ({ - fieldName, - label, - control, - errors, - options = [ - { id: "1", value: "yes", name: "Yes" }, - { id: "2", value: "no", name: "No" }, - ], -}: { - fieldName: Path; - label: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - control: Control; - errors: FieldErrors; - options?: OptionType[]; -}) => { - return ( - - - {label} - - ( - - field.onChange(e.target.value)} - sx={{ - display: "flex", - flexDirection: "row", - gap: 2, - }} - > - {options.map((option) => ( - } - label={option.name} - /> - ))} - - {!!errors?.[fieldName] && ( - - {errors?.[fieldName]?.message as string} - - )} - - )} - /> - - ); -}; - -export default ControllerRadioGroup; diff --git a/samples/SimpleFullStack/React/src/forms/controllers/ControllerSelect.tsx b/samples/SimpleFullStack/React/src/forms/controllers/ControllerSelect.tsx deleted file mode 100644 index 0856d24..0000000 --- a/samples/SimpleFullStack/React/src/forms/controllers/ControllerSelect.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { - FormControl, - FormControlProps, - MenuItem, - TextField, - TextFieldProps, -} from "@mui/material"; -import { Control, Controller, FieldErrors, FieldValues } from "react-hook-form"; - -const ControllerSelect = ({ - fieldName, - label, - props, - options, - control, - errors, - formControlProps, -}: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fieldName: any; - label: string; - options: { value: string | number; label: string }[]; - control: Control; - errors: FieldErrors; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - props?: TextFieldProps; // You can specify the type for props if needed - formControlProps?: FormControlProps; -}) => { - return ( - ( - - - {options.map((option) => ( - - {option.label} - - ))} - - - )} - /> - ); -}; - -export default ControllerSelect; diff --git a/samples/SimpleFullStack/React/src/forms/controllers/ControllerSelectDrowpDown.tsx b/samples/SimpleFullStack/React/src/forms/controllers/ControllerSelectDrowpDown.tsx deleted file mode 100644 index 9da48d5..0000000 --- a/samples/SimpleFullStack/React/src/forms/controllers/ControllerSelectDrowpDown.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { - Box, - FormControl, - FormHelperText, - FormLabel, - FormLabelProps, - InputLabel, - MenuItem, - Select, -} from "@mui/material"; -import { Control, Controller, FieldErrors, FieldValues } from "react-hook-form"; -import { OptionType } from "../../types/helperTypes"; - -const ControllerSelectDropDown = ({ - fieldName, - label, - control, - errors, - formLabel = { - name: "Select an option", - props: {}, - }, - options = [ - { id: "1", value: "yes", name: "Yes" }, - { id: "2", value: "no", name: "No" }, - ], -}: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fieldName: any; - label: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - control: Control; - formLabel?: { - name: string; - props: FormLabelProps; - }; - errors: FieldErrors; - options?: OptionType[]; -}) => { - return ( - - - {formLabel.name} - - - - {label} - - - ( - - )} - /> - - {errors?.[fieldName]?.message as string} - - - - ); -}; - -export default ControllerSelectDropDown; diff --git a/samples/SimpleFullStack/React/src/forms/controllers/ControllerTextField.tsx b/samples/SimpleFullStack/React/src/forms/controllers/ControllerTextField.tsx deleted file mode 100644 index cdbace3..0000000 --- a/samples/SimpleFullStack/React/src/forms/controllers/ControllerTextField.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { - FormControl, - FormControlProps, - FormLabel, - FormLabelProps, - TextField, - TextFieldProps, -} from "@mui/material"; -import { Control, Controller, FieldErrors, FieldValues } from "react-hook-form"; - -const ControllerTextField = ({ - fieldName, - label, - props, - control, - errors, - formLabel, - formControlProps, -}: { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - fieldName: any; - label: string; - props: TextFieldProps; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - control: Control; - errors: FieldErrors; - formLabel?: { - name: string; - props: FormLabelProps; - }; - formControlProps?: FormControlProps; -}) => { - return ( - - name={fieldName} - control={control} - render={({ field }) => ( - - {formLabel && ( - - {formLabel.name} - - )} - - - )} - /> - ); -}; - -export default ControllerTextField; diff --git a/samples/SimpleFullStack/React/src/global.css b/samples/SimpleFullStack/React/src/global.css deleted file mode 100644 index ce3fd4f..0000000 --- a/samples/SimpleFullStack/React/src/global.css +++ /dev/null @@ -1,3 +0,0 @@ -#root { - background: #f8f8f8; -} \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/helpers/formatDate.ts b/samples/SimpleFullStack/React/src/helpers/formatDate.ts deleted file mode 100644 index 8518805..0000000 --- a/samples/SimpleFullStack/React/src/helpers/formatDate.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const formatDate = (date: string) => { - const time = new Date(date).toLocaleTimeString('en-US'); - const dateStr = new Date(date).toLocaleDateString('en-US'); - - return `${dateStr} ${time}`; -}; diff --git a/samples/SimpleFullStack/React/src/main.tsx b/samples/SimpleFullStack/React/src/main.tsx deleted file mode 100644 index 7c359e9..0000000 --- a/samples/SimpleFullStack/React/src/main.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import App from "./App.tsx"; -import './global.css' - -const queryClient = new QueryClient(); - -ReactDOM.createRoot(document.getElementById("root")!).render( - - - - - -); diff --git a/samples/SimpleFullStack/React/src/pages/CompanyPage.tsx b/samples/SimpleFullStack/React/src/pages/CompanyPage.tsx deleted file mode 100644 index 3f1bfbb..0000000 --- a/samples/SimpleFullStack/React/src/pages/CompanyPage.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Box, CircularProgress, Typography, Grid } from "@mui/material"; -import { useParams } from "react-router-dom"; -import { useQuery } from '@tanstack/react-query'; -import { apiClient } from "service/axiosConfig"; -import { OrderRequest } from "types/order"; -import OrderCard from "./order/OrderCard"; // Import OrderCard component - -const fetchCompany = async (companyId: string) => { - const response = await apiClient.get( - `/api/orders/company?companyId=${companyId}` - ); - return response.data; -}; - -const CompanyPage = () => { - const { id } = useParams<{ id: string }>(); - const { data: ordersRequest, isLoading, error } = useQuery({ - queryKey: ["orders", id], - queryFn: () => fetchCompany(id || ""), - enabled: !!id, - }); - - if (isLoading) return ; - if (error) return Failed to load order data; - - return ( - - - Orders for Company {id} - - - {ordersRequest?.orders.map((order) => ( - - - - ))} - - - ); -}; - -export default CompanyPage; diff --git a/samples/SimpleFullStack/React/src/pages/Home.tsx b/samples/SimpleFullStack/React/src/pages/Home.tsx deleted file mode 100644 index b8ae7bf..0000000 --- a/samples/SimpleFullStack/React/src/pages/Home.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Divider, Grid } from "@mui/material" -// import CompaniesTable from "./company/CompaniesTable" -import ColorTextComponent from "./test/ColorTextComponent" - -const Home = () => { - return ( - - {/* */} - - - - - - ) -} - -export default Home \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/pages/NoContentPage.tsx b/samples/SimpleFullStack/React/src/pages/NoContentPage.tsx deleted file mode 100644 index 1df53e9..0000000 --- a/samples/SimpleFullStack/React/src/pages/NoContentPage.tsx +++ /dev/null @@ -1,7 +0,0 @@ -const NoContentPage = () => { - return ( -
Oops! Nothing to see here
- ); -}; - -export default NoContentPage; diff --git a/samples/SimpleFullStack/React/src/pages/company/CompaniesTable.test.tsx b/samples/SimpleFullStack/React/src/pages/company/CompaniesTable.test.tsx deleted file mode 100644 index c6316df..0000000 --- a/samples/SimpleFullStack/React/src/pages/company/CompaniesTable.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { render, screen, fireEvent } from '@testing-library/react'; -import CompaniesTable from './CompaniesTable'; -import '@testing-library/jest-dom'; -import { BrowserRouter } from 'react-router-dom'; - -// Mock the hooks and modules -vi.mock('store/useCompanies', () => ({ - useCompanies: () => ({ - companies: [ - { - id: '1', - tpid: 'TPID1', - accountName: 'Test Company', - segment: 'Segment1', - industry: 'Industry1', - subSegment: 'SubSegment1', - vertical: 'Vertical1' - } - ], - deleteCompany: vi.fn(), - }), -})); - -const mockOpenGlobalModal = vi.fn(); -const mockCloseGlobalModal = vi.fn(); - -// For default export mocking, return an object with a "default" key. -vi.mock('store/global/useGlobalModal', () => ({ - default: () => ({ - openGlobalModal: mockOpenGlobalModal, - closeGlobalModal: mockCloseGlobalModal, - }), -})); - -const mockNavigate = vi.fn(); -vi.mock('react-router-dom', async () => { - const actual = await vi.importActual('react-router-dom'); - return { - ...actual, - useNavigate: () => mockNavigate, - }; -}); - -// Wrap component with BrowserRouter for routing context -const renderComponent = (search?: string) => { - return render( - - - - ); -}; - -describe('CompaniesTable Component', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it('renders without crashing and shows the companies table', () => { - renderComponent(); - const tableElement = screen.getByTestId('companies-table'); - expect(tableElement).toBeInTheDocument(); - }); - - it('navigates to company details on row click', () => { - renderComponent(); - // Use the company name as a proxy for the row click - const rowElement = screen.getByText('Test Company'); - fireEvent.click(rowElement); - expect(mockNavigate).toHaveBeenCalledWith('/company/1'); - }); - - it('opens global modal when delete button is clicked', () => { - renderComponent(); - const deleteButton = screen.getByTestId('delete-button'); - fireEvent.click(deleteButton); - expect(mockOpenGlobalModal).toHaveBeenCalled(); - const modalCallArg = mockOpenGlobalModal.mock.calls[0][0]; - expect(modalCallArg.title).toBe('Remove Company'); - expect(modalCallArg.content).toBe('Confirm you want to remove this company.'); - }); -}); diff --git a/samples/SimpleFullStack/React/src/pages/company/CompaniesTable.tsx b/samples/SimpleFullStack/React/src/pages/company/CompaniesTable.tsx deleted file mode 100644 index 518e32c..0000000 --- a/samples/SimpleFullStack/React/src/pages/company/CompaniesTable.tsx +++ /dev/null @@ -1,153 +0,0 @@ -import { useNavigate } from 'react-router-dom'; -import DeleteIcon from '@mui/icons-material/Delete'; -import EditIcon from '@mui/icons-material/Edit'; -import Box from '@mui/material/Box'; -import IconButton from '@mui/material/IconButton'; -import { GridColDef, GridRowParams } from '@mui/x-data-grid'; -import useGlobalModal from 'store/global/useGlobalModal'; -import CompanyForm from './CompanyForm'; -import GenericDataGrid from 'components/generics/GenericDataGrid'; -import { Company } from 'types/company'; -import { useCompanies } from 'store/useCompanies'; - -const CompaniesTable = ({ search }: { search?: string }) => { - const nav = useNavigate(); - const { openGlobalModal, closeGlobalModal } = useGlobalModal(); - const { companies: data, deleteCompany } = useCompanies(); - - const filteredCompanies = data?.filter(company => - company?.accountName?.toLowerCase().includes(search?.toLowerCase() || '') - ); - - const onRowClick = (params: GridRowParams) => { - nav(`/company/${params.row.id}`); - }; - - const handleDelete = (id: string) => { - openGlobalModal({ - content: 'Confirm you want to remove this company.', - title: `Remove Company`, - primaryAction: () => { - deleteCompany(id); - closeGlobalModal(); - }, - primaryActionText: 'Remove', - dialogProps: { - maxWidth: 'sm', - fullWidth: true, - } - }); - }; - - const handleEdit = (data: Company) => { - if (data) { - openGlobalModal({ - content: ( - - ), - title: `Edit Company`, - formName: "company-form", - isDirtyCheck: true, - }); - } - }; - - const columns: GridColDef[] = [ - { field: 'tpid', headerName: 'TPID' }, - { - field: 'accountName', - headerName: 'Account Name', - flex: 1, - }, - { - field: 'industry', - headerName: 'Industry', - flex: 1, - }, - { - field: 'vertical', - headerName: 'Vertical', - flex: 1, - }, - { - field: 'segment', - headerName: 'Segment', - flex: 1, - }, - { - field: 'subSegment', - headerName: 'Sub Segment', - flex: 1, - }, - { - field: 'action', - headerName: '', - width: 150, - renderCell: params => { - return ( - - { - e.stopPropagation(); - handleEdit(params.row); - }} - > - - - { - e.stopPropagation(); - handleDelete(params.row.id); - }} - > - - - - ); - }, - }, - ]; - - return ( -
- -
- ); -}; - -export default CompaniesTable; diff --git a/samples/SimpleFullStack/React/src/pages/company/CompanyForm.tsx b/samples/SimpleFullStack/React/src/pages/company/CompanyForm.tsx deleted file mode 100644 index 8bb659a..0000000 --- a/samples/SimpleFullStack/React/src/pages/company/CompanyForm.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; - -import Box from "@mui/material/Box"; - -import ControllerTextField from "forms/controllers/ControllerTextField"; -import ControllerSelect from "forms/controllers/ControllerSelect"; -import { verticalOptions } from "constants/companyDropdownOptions"; -import { Company, initialCompany } from "types/company"; -import { useEffect } from "react"; -import { useFormConfig } from "store/useFormConfigs"; -import { useCompanies } from "store/useCompanies"; -import useGlobalModal from "store/global/useGlobalModal"; - -const formSchema = z.object({ - tpid: z.string().min(1, { message: "Required" }), - accountName: z.string().min(1, { message: "Required" }), - segment: z.string().min(1, { message: "Required" }), - industry: z.string().min(1, { message: "Required" }), - subSegment: z.string().min(1, { message: "Required" }), - vertical: z.string().min(1, { message: "Required" }), -}); - -const CompanyForm = ({ - initialData, -}: { - initialData?: Company; -}) => { - const { setIsDirty } = useFormConfig(); - const { addCompany, editCompany, companies } = useCompanies(); - const { closeGlobalModal } = useGlobalModal(); - - const { - handleSubmit, - control, - formState: { errors, isDirty }, - } = useForm({ - defaultValues: initialData || { - ...initialCompany, - }, - mode: "onSubmit", - reValidateMode: "onChange", - resolver: zodResolver(formSchema), - }); - - const handleOnSubmit = handleSubmit((data) => { - if (initialData) { - editCompany(initialData.id, data); - closeGlobalModal(); - return; - } - const newData: Company = { - ...initialCompany, - ...data, - id: companies.length.toString(), - }; - - addCompany(newData); - closeGlobalModal(); - }); - - useEffect(() => { - setIsDirty(isDirty); - }, [isDirty, setIsDirty]); - - return ( -
- - - fieldName={`tpid`} - label="TPID" - control={control} - errors={errors} - props={{ variant: "outlined", fullWidth: true, size: "small" }} - /> - - - fieldName={`accountName`} - label="Account Name" - control={control} - errors={errors} - props={{ variant: "outlined", fullWidth: true, size: "small" }} - /> - - - fieldName={`segment`} - label="Segment" - control={control} - errors={errors} - props={{ variant: "outlined", fullWidth: true, size: "small" }} - /> - - - fieldName={`industry`} - label="Industry" - control={control} - errors={errors} - props={{ variant: "outlined", fullWidth: true, size: "small" }} - /> - - - fieldName={`subSegment`} - label="Sub Segment" - control={control} - errors={errors} - props={{ variant: "outlined", fullWidth: true, size: "small" }} - /> - - - fieldName="vertical" - label="Vertical" - options={verticalOptions} - props={{ variant: "outlined", fullWidth: true, size: "small" }} - control={control} - errors={errors} - /> - -
- ); -}; - -export default CompanyForm; diff --git a/samples/SimpleFullStack/React/src/pages/order/OrderCard.test.tsx b/samples/SimpleFullStack/React/src/pages/order/OrderCard.test.tsx deleted file mode 100644 index 82dfcab..0000000 --- a/samples/SimpleFullStack/React/src/pages/order/OrderCard.test.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import OrderCard from "./OrderCard"; -import "@testing-library/jest-dom"; - -describe("OrderCard Component", () => { - it("renders without crashing", () => { - render( - - ); - const boxElement = screen.getByRole("presentation"); - expect(boxElement).toBeInTheDocument(); - }); - -}); \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/pages/order/OrderCard.tsx b/samples/SimpleFullStack/React/src/pages/order/OrderCard.tsx deleted file mode 100644 index a4b6d79..0000000 --- a/samples/SimpleFullStack/React/src/pages/order/OrderCard.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Box, Card, CardContent, Typography, Button } from "@mui/material"; - -interface OrderCardProps { - orderId: string; - customerName: string; - orderDate: string; - totalAmount: string; -} - -const OrderCard = ({ orderId, customerName, orderDate, totalAmount }: OrderCardProps) => { - return ( - - - - Order ID: {orderId} - - - Customer: {customerName} - - - Date: {orderDate} - - - Total: {totalAmount} - - - - - - - ); -}; - -export default OrderCard; \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.test.txt b/samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.test.txt deleted file mode 100644 index d74b11d..0000000 --- a/samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.test.txt +++ /dev/null @@ -1,26 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import ColorTextComponent from "./ColorTextComponent"; - -describe("ColorTextComponent", () => { - it("renders the amount in green when the amount is greater than 0", () => { - render(); - const textElement = screen.getByText("10"); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveStyle("color: rgb(0, 128, 0)"); - }); - - it("renders the amount in red when the amount is less than 0", () => { - render(); - const textElement = screen.getByText("-5"); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveStyle("color: rgb(255, 0, 0)"); - }); - - it("renders the amount in gray when the amount is 0", () => { - render(); - const textElement = screen.getByText("0"); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveStyle("color: rgb(128, 128, 128)"); - }); -}); - diff --git a/samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.tsx b/samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.tsx deleted file mode 100644 index 68fc396..0000000 --- a/samples/SimpleFullStack/React/src/pages/test/ColorTextComponent.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Box, Typography } from "@mui/material"; - -const ColorTextComponent = ({ - amount -}: { - amount: number -}) => { - - const determineColor = (amount: number) => { - if (amount > 0) { - return "rgb(0, 128, 0)"; - } else if (amount < 0) { - return "rgb(255, 0, 0)"; - } else { - return "rgb(128, 128, 128)"; - } - } - - return ( - - - {amount} - - - ) -} - -export default ColorTextComponent \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.test.tsx b/samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.test.tsx deleted file mode 100644 index c8c829f..0000000 --- a/samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.test.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { render, screen } from "@testing-library/react"; -import "@testing-library/jest-dom"; -import CompanyAndIndustry, { companyAndIndustries } from "./answer/AnswerCompanyAndIndustry"; -import { describe, it, expect } from "vitest"; - -describe("CompanyAndIndustry Component", () => { - it("renders the heading 'Company and Industry'", () => { - render(); - expect(screen.getByText("Company and Industry")).toBeInTheDocument(); - }); - - it("displays the correct company name", () => { - render(); - expect(screen.getByText("Company Name: apple")).toBeInTheDocument(); - }); - - it("displays the correct industry for a known company", () => { - render(); - expect(screen.getByText("Industry: AI")).toBeInTheDocument(); - }); - - it("displays 'Unknown Industry' for an unknown company", () => { - render(); - expect(screen.getByText("Industry: Unknown Industry")).toBeInTheDocument(); - }); - - it("handles case-insensitive company names", () => { - render(); - expect(screen.getByText("Industry: AI")).toBeInTheDocument(); - }); - - it("displays 'Unknown Industry' when no company name is provided", () => { - render(); - expect(screen.getByText("Industry: Unknown Industry")).toBeInTheDocument(); - }); - - it("matches the industry data structure", () => { - expect(companyAndIndustries).toEqual([ - { industry: "AI", companies: ["apple", "google", "microsoft"] }, - { industry: "Finance", companies: ["goldman sachs", "jp morgan", "bank of america"] }, - { industry: "Retail", companies: ["walmart", "target", "costco"] }, - ]); - }); -}); diff --git a/samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.tsx b/samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.tsx deleted file mode 100644 index 21264f7..0000000 --- a/samples/SimpleFullStack/React/src/pages/test/CompanyAndIndustry.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; - -export const companyAndIndustries = [ - { - industry: "AI", - companies: ["apple", "google", "microsoft"], - }, - { - industry: "Finance", - companies: ["goldman sachs", "jp morgan", "bank of america"], - }, - { - industry: "Retail", - companies: ["walmart", "target", "costco"], - }, -]; - -interface CompanyAndIndustryProps { - companyName: string; -} - -const CompanyAndIndustry: React.FC = ({ companyName }) => { - const normalizedCompanyName = companyName.toLowerCase(); - const industry = companyAndIndustries.find((entry) => - entry.companies.includes(normalizedCompanyName) - )?.industry || "Unknown Industry"; - - return ( -
-

Company and Industry

-

Company Name: {companyName}

-

Industry: {industry}

-
- ); -}; - -export default CompanyAndIndustry; diff --git a/samples/SimpleFullStack/React/src/pages/test/answer/AnswerColorTextComponent.test.tsx b/samples/SimpleFullStack/React/src/pages/test/answer/AnswerColorTextComponent.test.tsx deleted file mode 100644 index 779bd7c..0000000 --- a/samples/SimpleFullStack/React/src/pages/test/answer/AnswerColorTextComponent.test.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { render, screen } from '@testing-library/react'; -import '@testing-library/jest-dom'; -import ColorTextComponent from '../ColorTextComponent'; - -describe('ColorTextComponent', () => { - it('displays the amount in green when amount is positive', () => { - render(); - const textElement = screen.getByText('10'); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveStyle({ color: 'rgb(0, 128, 0)' }); - }); - - it('displays the amount in red when amount is negative', () => { - render(); - const textElement = screen.getByText('-5'); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveStyle({ color: 'rgb(255, 0, 0)' }); - }); - - it('displays the amount in gray when amount is zero', () => { - render(); - const textElement = screen.getByText('0'); - expect(textElement).toBeInTheDocument(); - expect(textElement).toHaveStyle({ color: 'rgb(128, 128, 128)' }); - }); -}); diff --git a/samples/SimpleFullStack/React/src/pages/test/answer/AnswerCompanyAndIndustry.tsx b/samples/SimpleFullStack/React/src/pages/test/answer/AnswerCompanyAndIndustry.tsx deleted file mode 100644 index 3c5d0ca..0000000 --- a/samples/SimpleFullStack/React/src/pages/test/answer/AnswerCompanyAndIndustry.tsx +++ /dev/null @@ -1,42 +0,0 @@ - -export const companyAndIndustries = [ - { - industry: "AI", - companies: ["apple", "google", "microsoft"], - }, - { - industry: "Finance", - companies: ["goldman sachs", "jp morgan", "bank of america"], - }, - { - industry: "Retail", - companies: ["walmart", "target", "costco"], - }, -] - -const CompanyAndIndustry = ({ - companyName -}: - { - companyName: string; - }) => { - - - const findIndustry = (companyName: string) => { - const industry = companyAndIndustries.find(industry => industry.companies.includes(companyName.toLowerCase())); - return industry ? industry.industry : "Unknown Industry"; - } - - - return ( - -
-

Company and Industry

-

Company Name: {companyName}

-

Industry: {findIndustry(companyName)}

-
- ) - -} - -export default CompanyAndIndustry; \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/routes/privateRoutes.tsx b/samples/SimpleFullStack/React/src/routes/privateRoutes.tsx deleted file mode 100644 index c2d6cac..0000000 --- a/samples/SimpleFullStack/React/src/routes/privateRoutes.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { RouteObject } from 'react-router-dom'; -import { PageContainer } from 'components/layouts/PageContainer'; - - -const myRoutes: RouteObject[] = [ - { - path: '/', - element: , - children: [ - { - path: '/', - lazy: async () => { - const Component = await import('../pages/Home') - return { Component: Component.default }; - }, - }, - { - path: '/company/:id', - lazy: async () => { - const Component = await import('../pages/CompanyPage'); - return { Component: Component.default }; - }, - }, - ], - }, - { - path: '*', - element: , - children: [ - { - path: '*', - lazy: async () => { - const Component = await import('../pages/NoContentPage'); - return { Component: Component.default }; - }, - }, - ], - }, -]; - -export { myRoutes }; diff --git a/samples/SimpleFullStack/React/src/service/axiosConfig.ts b/samples/SimpleFullStack/React/src/service/axiosConfig.ts deleted file mode 100644 index e239537..0000000 --- a/samples/SimpleFullStack/React/src/service/axiosConfig.ts +++ /dev/null @@ -1,14 +0,0 @@ -import axios from 'axios'; - -export const apiClient = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL || '', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, -}); - -//middle ware for setting headers. -apiClient.interceptors.request.use(config => { - return config; -}); diff --git a/samples/SimpleFullStack/React/src/service/crud/useDelete.ts b/samples/SimpleFullStack/React/src/service/crud/useDelete.ts deleted file mode 100644 index 4244e48..0000000 --- a/samples/SimpleFullStack/React/src/service/crud/useDelete.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import useSnackbar from '../../store/global/useGlobalSnackbar'; -import { apiClient } from '../axiosConfig'; - -const useDelete = ({ - url, - queryKey, - snackMessage = 'Deleted successfully', - onSuccess, - onError, -}: { - url: string; - queryKey: string[]; - snackMessage?: string; - onSuccess?: () => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onError?: (error: any) => void; -}) => { - const queryClient = useQueryClient(); - const snackbar = useSnackbar(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const params: Record = {}; - - const master_code = import.meta.env.VITE_AZURE_FUNCTION_MASTER_CODE || ''; - - if (master_code) { - params.code = master_code; - } - - const deleteMutation = useMutation({ - mutationFn: async (id: string) => { - const response = await apiClient.delete(url, { - params: { - ...params, - id, - }, - }); - - return response.data; - }, - - onSuccess: () => { - if (onSuccess) { - onSuccess(); - } - - snackbar.open(snackMessage, 'success'); - - //refetch categories - queryClient.refetchQueries({ - queryKey, - }); - }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onError: (error: any) => { - snackbar.open(error.message, 'error'); - - if (onError) { - onError(error); - } - }, - }); - - return { deleteMutation }; -}; -export default useDelete; diff --git a/samples/SimpleFullStack/React/src/service/crud/useFetch.ts b/samples/SimpleFullStack/React/src/service/crud/useFetch.ts deleted file mode 100644 index 75154f1..0000000 --- a/samples/SimpleFullStack/React/src/service/crud/useFetch.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { apiClient } from '../axiosConfig'; -import { DEFAULT_PAGING_OPTIONS, FetchParams } from 'types/helperTypes'; - -const useFetch = ({ - url, - config, - queryKey, - search, - pagingOptions = DEFAULT_PAGING_OPTIONS, - shouldFetch = true, - refetchOnWindowFocus = false, - refetchOnReconnect = true, - mockData, -}: FetchParams) => { - // Modify queryKey based on search and pagingOptions - const finalQueryKey = [...queryKey]; - - if (search) { - finalQueryKey.push(search); - } - - if (pagingOptions) { - if (pagingOptions.page) { - finalQueryKey.push(pagingOptions.page.toString()); - } - if (pagingOptions.pageSize) { - finalQueryKey.push(pagingOptions.pageSize.toString()); - } - } - - // Fetch - const { isLoading, isError, data } = useQuery({ - queryKey: finalQueryKey, // Use the updated queryKey - queryFn: async () => { - if (!shouldFetch) { - return null; - } - // Build the params object dynamically - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const params: Record = {}; - - if (search) { - params.search = search; - } - - if (pagingOptions) { - params.page = pagingOptions.page; - params.pageSize = pagingOptions.pageSize; - } - - if (config?.params) { - config.params = { - ...config.params, - ...params, - }; - } - - //mocking data - if (mockData) { - return mockData; - } - - // Make the API request with the built params object - const response = await apiClient.get(url, { - params, - ...config, - }); - - return response.data as TReturn; - }, - enabled: shouldFetch, - refetchOnWindowFocus, - refetchOnReconnect, - }); - - return { isLoading, isError, data }; -}; - -export default useFetch; diff --git a/samples/SimpleFullStack/React/src/service/crud/usePost.ts b/samples/SimpleFullStack/React/src/service/crud/usePost.ts deleted file mode 100644 index 5d11dfa..0000000 --- a/samples/SimpleFullStack/React/src/service/crud/usePost.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import useSnackbar from '../../store/global/useGlobalSnackbar'; -import { apiClient } from '../axiosConfig'; - -const usePost = ({ - url, - queryKey, - onSuccess, - onError, - snackbarMessage, -}: { - url: string; - queryKey: string[]; - onSuccess?: (data: T) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onError?: (error: any) => void; - snackbarMessage?: string; -}) => { - const snackbar = useSnackbar(); - const queryClient = useQueryClient(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const params: Record = {}; - - const master_code = import.meta.env.VITE_AZURE_FUNCTION_MASTER_CODE || ''; - - if (master_code) { - params.code = master_code; - } - - //Post - const postMutation = useMutation({ - mutationFn: async (data: T) => { - const response = await apiClient.post(url, data, { - params, - }); - return response.data as T; - }, - onSuccess: (data: T) => { - snackbar.open(snackbarMessage || 'Successfully added', 'success'); - - if (onSuccess) { - onSuccess(data); - } - - //refetch categories - queryClient.refetchQueries({ - queryKey, - }); - - //refetch item - queryClient.refetchQueries({ - queryKey: [...queryKey, data.id], - }); - }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onError: (error: any) => { - snackbar.open(error.message, 'error'); - - if (onError) { - onError(error); - } - }, - }); - - return { postMutation }; -}; -export default usePost; diff --git a/samples/SimpleFullStack/React/src/service/crud/useUpsert.ts b/samples/SimpleFullStack/React/src/service/crud/useUpsert.ts deleted file mode 100644 index 9cd97f3..0000000 --- a/samples/SimpleFullStack/React/src/service/crud/useUpsert.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import useSnackbar from '../../store/global/useGlobalSnackbar'; -import { apiClient } from '../axiosConfig'; - -const useUpsert = ({ - url, - queryKey, - onSuccess, - onError, - snackbarMessage, -}: { - url: string; - queryKey: string[]; - onSuccess?: (data: T) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onError?: (error: any) => void; - snackbarMessage?: string; -}) => { - const snackbar = useSnackbar(); - const queryClient = useQueryClient(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const params: Record = {}; - - const master_code = import.meta.env.VITE_AZURE_FUNCTION_MASTER_CODE || ''; - - if (master_code) { - params.code = master_code; - } - - //Post - const upsertMutation = useMutation({ - mutationFn: async (data: T) => { - const response = await apiClient.put(url, data, { - params, - }); - return response.data as T; - }, - onSuccess: (data: T) => { - snackbar.open(snackbarMessage || 'Successfully added', 'success'); - - if (onSuccess) { - onSuccess(data); - } - - //refetch categories - queryClient.refetchQueries({ - queryKey, - }); - - //refetch item - queryClient.refetchQueries({ - queryKey: [...queryKey, data.id], - }); - }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - onError: (error: any) => { - snackbar.open(error.message, 'error'); - - if (onError) { - onError(error); - } - }, - }); - - return { upsertMutation }; -}; -export default useUpsert; diff --git a/samples/SimpleFullStack/React/src/service/hooks/useDebounce.ts b/samples/SimpleFullStack/React/src/service/hooks/useDebounce.ts deleted file mode 100644 index ef1d148..0000000 --- a/samples/SimpleFullStack/React/src/service/hooks/useDebounce.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useEffect, useState } from 'react'; - -export default function useDebounce(val: string, delay: number) { - const [debounceValue, setDebounceValue] = useState(val); - useEffect(() => { - const handler = setTimeout(() => { - setDebounceValue(val); - }, delay); - - return () => { - clearTimeout(handler); - }; - }, [val, delay]); - return debounceValue; -} diff --git a/samples/SimpleFullStack/React/src/store/global/useGlobalModal.ts b/samples/SimpleFullStack/React/src/store/global/useGlobalModal.ts deleted file mode 100644 index c9688c1..0000000 --- a/samples/SimpleFullStack/React/src/store/global/useGlobalModal.ts +++ /dev/null @@ -1,96 +0,0 @@ -import React from 'react'; -import { create } from 'zustand'; -import { DialogProps } from '@mui/material/Dialog'; -import { theme } from '../../styles/themes'; - -export type CustomModalProps = { - title?: string; - titleColor?: string; - primaryAction?: () => void; - primaryActionText?: string; - closeAction?: () => void; - formName?: string; // used to trigger form submit from outside form -}; - -export type OpenModalProps = CustomModalProps & { - content: React.ReactNode; - additionalActions?: React.ReactNode; - dialogProps?: Partial; // Allow custom MUI Dialog props - isDirtyCheck?: boolean; // Check for unsaved changes -}; - -type GlobalModalStore = { - state: { - content: React.ReactNode | null; - additionalActions: React.ReactNode | null; - dialogProps: DialogProps; // Now holds isOpen and all Dialog properties - isDirtyCheck: boolean; - } & CustomModalProps; - openGlobalModal: (params: OpenModalProps) => void; - closeGlobalModal: () => void; -}; - -const useGlobalModal = create(set => ({ - state: { - content: null, - additionalActions: null, - title: '', - titleColor: theme.palette.primary.main, - primaryAction: undefined, - primaryActionText: undefined, - closeAction: undefined, - dialogProps: { - open: false, // isOpen moved inside dialogProps - fullWidth: true, - maxWidth: 'sm', // Default modal size - }, - formName: undefined, - isDirtyCheck: false, - }, - - openGlobalModal: ({ - content, - title, - titleColor, - primaryAction, - primaryActionText, - closeAction, - dialogProps = {}, - formName, - additionalActions, - isDirtyCheck, - }) => - set(state => ({ - state: { - ...state.state, - content, - title, - titleColor: titleColor ?? theme.palette.primary.main, - primaryAction, - primaryActionText, - closeAction, - dialogProps: { ...state.state.dialogProps, ...dialogProps, open: true }, // Ensure open is true - formName, - additionalActions, - isDirtyCheck: isDirtyCheck ?? false, - }, - })), - - closeGlobalModal: () => - set(state => ({ - state: { - ...state.state, - content: null, - title: '', - primaryAction: undefined, - primaryActionText: undefined, - closeAction: undefined, - dialogProps: { ...state.state.dialogProps, open: false }, // Ensure open is false - formName: undefined, - additionalActions: null, - isDirtyCheck: false, - }, - })), -})); - -export default useGlobalModal; diff --git a/samples/SimpleFullStack/React/src/store/global/useGlobalSecondaryModal.ts b/samples/SimpleFullStack/React/src/store/global/useGlobalSecondaryModal.ts deleted file mode 100644 index 2d5b076..0000000 --- a/samples/SimpleFullStack/React/src/store/global/useGlobalSecondaryModal.ts +++ /dev/null @@ -1,79 +0,0 @@ -import React from 'react'; -import { create } from 'zustand'; -import { DialogProps } from '@mui/material/Dialog'; -import { theme } from '../../styles/themes'; -import { CustomModalProps, OpenModalProps } from './useGlobalModal'; - -type GlobalSecodaryModalStore = { - globalSecondaryState: { - content: React.ReactNode | null; - dialogProps: DialogProps; // Now holds isOpen and all Dialog properties - } & CustomModalProps; - openSecondaryModal: (params: OpenModalProps) => void; - closeSecondaryModal: () => void; -}; - -const useGlobalModal = create(set => ({ - globalSecondaryState: { - content: null, - title: '', - titleColor: theme.palette.primary.main, - primaryAction: undefined, - primaryActionText: undefined, - closeAction: undefined, - dialogProps: { - open: false, // isOpen moved inside dialogProps - fullWidth: true, - maxWidth: 'sm', // Default modal size - }, - formName: undefined, - isDirty: false, - }, - - openSecondaryModal: ({ - content, - title, - titleColor, - primaryAction, - primaryActionText, - closeAction, - dialogProps = {}, - formName, - }) => - set(state => ({ - globalSecondaryState: { - ...state.globalSecondaryState, - content, - title, - titleColor: titleColor ?? theme.palette.primary.main, - primaryAction, - primaryActionText, - closeAction, - dialogProps: { - ...state.globalSecondaryState.dialogProps, - ...dialogProps, - open: true, - }, // Ensure open is true - formName, - }, - })), - - closeSecondaryModal: () => - set(state => ({ - globalSecondaryState: { - ...state.globalSecondaryState, - content: null, - title: '', - primaryAction: undefined, - primaryActionText: undefined, - closeAction: undefined, - dialogProps: { - ...state.globalSecondaryState.dialogProps, - open: false, - }, // Ensure open is false - formName: undefined, - }, - })), -})); - -export default useGlobalModal; diff --git a/samples/SimpleFullStack/React/src/store/global/useGlobalSidePanel.ts b/samples/SimpleFullStack/React/src/store/global/useGlobalSidePanel.ts deleted file mode 100644 index 45ebaf9..0000000 --- a/samples/SimpleFullStack/React/src/store/global/useGlobalSidePanel.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { create } from 'zustand'; - -type OpenSidePanelParams = { - content: JSX.Element; - title: string; -} - -type SidePanelStore = { - isOpen: boolean; - content: JSX.Element | null; - title: string; - open: (params: OpenSidePanelParams) => void; - close: () => void; -}; - -const useGlobalSidePanel = create(set => ({ - isOpen: false, - content: null, - title: '', - open: ({ content, title }) => set({ isOpen: true, content, title}), - close: () => set({ isOpen: false, content: null }), -})); - -export default useGlobalSidePanel; \ No newline at end of file diff --git a/samples/SimpleFullStack/React/src/store/global/useGlobalSnackbar.ts b/samples/SimpleFullStack/React/src/store/global/useGlobalSnackbar.ts deleted file mode 100644 index 29de2d2..0000000 --- a/samples/SimpleFullStack/React/src/store/global/useGlobalSnackbar.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { AlertColor } from '@mui/material'; -import { create } from 'zustand'; - -type GlobalSnackbarStore = { - isOpen: boolean; - message: string; - severity: AlertColor; - vertical?: string; - horizontal?: string; - - open: ( - message: string, - severity: AlertColor, - vertical?: string, - horizonal?: string, - ) => void; - close: () => void; -}; - -const useGlobalSnackbar = create(set => ({ - isOpen: false, - message: '', - severity: 'success', - vertical: 'bottom', - horizontal: 'center', - - open: (message, severity, vertical = 'bottom', horizontal = 'center') => - set({ isOpen: true, message, severity, vertical, horizontal }), - close: () => set({ isOpen: false, message: '', severity: 'success' }), -})); - -export default useGlobalSnackbar; diff --git a/samples/SimpleFullStack/React/src/store/useAppStore.ts b/samples/SimpleFullStack/React/src/store/useAppStore.ts deleted file mode 100644 index a164aad..0000000 --- a/samples/SimpleFullStack/React/src/store/useAppStore.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { create } from "zustand"; - -type AppState = { - isDarkMode: boolean; - toggleDarkMode: () => void; -}; - -export const useAppStore = create((set) => ({ - isDarkMode: false, - toggleDarkMode: () => set((state) => ({ isDarkMode: !state.isDarkMode })), -})); diff --git a/samples/SimpleFullStack/React/src/store/useCompanies.ts b/samples/SimpleFullStack/React/src/store/useCompanies.ts deleted file mode 100644 index 4e2f3d5..0000000 --- a/samples/SimpleFullStack/React/src/store/useCompanies.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { mockCompanies } from 'constants/mockCompanies'; -import { Company } from 'types/company'; -import create from 'zustand'; - -type CompaniesState = { - companies: Company[]; - addCompany: (company: Company) => void; - editCompany: (id: string, updatedCompany: Partial) => void; - deleteCompany: (id: string) => void; -}; - -export const useCompanies = create(set => ({ - companies: mockCompanies, - addCompany: company => - set(state => ({ - companies: [...state.companies, company], - })), - editCompany: (id, updatedCompany) => - set(state => ({ - companies: state.companies.map(company => - company.id === id ? { ...company, ...updatedCompany } : company, - ), - })), - deleteCompany: id => - set(state => ({ - companies: state.companies.filter(company => company.id !== id), - })), -})); diff --git a/samples/SimpleFullStack/React/src/store/useFormConfigs.ts b/samples/SimpleFullStack/React/src/store/useFormConfigs.ts deleted file mode 100644 index de97ef5..0000000 --- a/samples/SimpleFullStack/React/src/store/useFormConfigs.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { create } from 'zustand'; - -type FormConfigStore = { - isDirty: boolean; - setIsDirty: (isDirty: boolean) => void; -}; - -export const useFormConfig = create(set => ({ - isDirty: false, - setIsDirty: isDirty => set({ isDirty }), -})); diff --git a/samples/SimpleFullStack/React/src/store/useTableSearch.ts b/samples/SimpleFullStack/React/src/store/useTableSearch.ts deleted file mode 100644 index 831b938..0000000 --- a/samples/SimpleFullStack/React/src/store/useTableSearch.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { create } from "zustand"; - -type TableSearch = { - search: string; - setSearch: (search: string) => void; - pagingOptions: { - page: number; - pageSize: number; - }; - setPagingOptions: (pagingOptions: { page: number; pageSize: number }) => void; - setPagingOptiongDefault: () => void; -}; - -export const useTableSearch = create((set) => ({ - search: "", - setSearch: (search) => set({ search }), - pagingOptions: { - page: 1, - pageSize: 10, - }, - setPagingOptions: (pagingOptions) => set({ pagingOptions }), - setPagingOptiongDefault: () => - set({ pagingOptions: { page: 1, pageSize: 10 } }), -})); diff --git a/samples/SimpleFullStack/React/src/styles/themes.ts b/samples/SimpleFullStack/React/src/styles/themes.ts deleted file mode 100644 index a52773e..0000000 --- a/samples/SimpleFullStack/React/src/styles/themes.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { createTheme } from '@mui/material/styles'; - -// Theme Types: Custom colors -export interface CustomPaletteOptions { - darkGrey: string; - white: string; - dodgerBlue: string; -} - -export const theme = createTheme({ - spacing: 8, - palette: { - mode: 'light', - custom: { - darkGrey: '#8C8C8C', - white: '#FFFFFF', - dodgerBlue: '#2196F3', - }, - primary: { - main: '#00467F', - light: '#027AA9', - }, - warning: { - main: '#F4802B', - }, - grey: { - 400: '#BDBDBD', - }, - }, - components: { - MuiDialogTitle: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.primary.main, // Use theme primary color - color: theme.palette.primary.contrastText, // Use theme white color - }), - }, - }, - MuiAppBar: { - styleOverrides: { - root: ({ theme }) => ({ - backgroundColor: theme.palette.background.default, // Use theme primary color - color: theme.palette.text.primary, // Use theme white color - }), - }, - }, - MuiButton: { - styleOverrides: { - contained: { - '&:hover': { - background: '#027AA9', - color: '#FFFFFF', - }, - }, - text: { - color: '#2196F3', - textTransform: 'none', - }, - }, - }, - }, -}); diff --git a/samples/SimpleFullStack/React/src/types/company.ts b/samples/SimpleFullStack/React/src/types/company.ts deleted file mode 100644 index 1650b8a..0000000 --- a/samples/SimpleFullStack/React/src/types/company.ts +++ /dev/null @@ -1,19 +0,0 @@ -export type Company = { - id: string; - tpid?: string; - accountName?: string; - segment?: string; - industry?: string; - subSegment?: string; - vertical?: string; -}; - -export const initialCompany: Company = { - id: '-1', - tpid: '', - accountName: '', - segment: '', - subSegment: '', - industry: '', - vertical: '', -}; diff --git a/samples/SimpleFullStack/React/src/types/helperTypes.ts b/samples/SimpleFullStack/React/src/types/helperTypes.ts deleted file mode 100644 index 71817c6..0000000 --- a/samples/SimpleFullStack/React/src/types/helperTypes.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { AxiosRequestConfig } from 'axios'; - -export type Prettify = { [K in keyof T]: T[K] } & object; - -export type OptionType = { - id: string; - name?: string; - label?: string; - value: string; -}; - -export type FetchParams = { - url: string; - config?: AxiosRequestConfig | undefined; - queryKey: string[]; - search?: string; - pagingOptions?: { - page: number; - pageSize: number; - }; - shouldFetch?: boolean; - refetchOnWindowFocus?: boolean; - refetchOnReconnect?: boolean; - mockData?: TReturn; -}; - -export type PagingOptions = { - page: number; - pageSize: number; -}; - -export const DEFAULT_PAGING_OPTIONS: PagingOptions = { - page: 1, - pageSize: 25, -}; diff --git a/samples/SimpleFullStack/React/src/types/order.ts b/samples/SimpleFullStack/React/src/types/order.ts deleted file mode 100644 index 79b6a3b..0000000 --- a/samples/SimpleFullStack/React/src/types/order.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type Order = { - orderId: string; - companyId: string; - customerName: string; - orderDate: string; // ISO 8601 format - totalAmount: number; -}; - -export type OrderRequest = { - orders: Order[]; - totalAmount: number; -}; diff --git a/samples/SimpleFullStack/React/src/types/theme.d.ts b/samples/SimpleFullStack/React/src/types/theme.d.ts deleted file mode 100644 index 861eead..0000000 --- a/samples/SimpleFullStack/React/src/types/theme.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { CustomPaletteOptions } from '../styles/themes'; // Adjust the import path according to your project structure - -declare module '@mui/material/styles' { - interface Palette { - custom: CustomPaletteOptions; - } - - interface PaletteOptions { - custom?: CustomPaletteOptions; - } -} diff --git a/samples/SimpleFullStack/React/staticwebapp.config.json b/samples/SimpleFullStack/React/staticwebapp.config.json deleted file mode 100644 index c063327..0000000 --- a/samples/SimpleFullStack/React/staticwebapp.config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "routes": [ - { - "route": "/*", - "allowedRoles": [ - "anonymous" - ] - } - ], - "navigationFallback": { - "rewrite": "/index.html" - } -} \ No newline at end of file diff --git a/samples/SimpleFullStack/React/tsconfig.json b/samples/SimpleFullStack/React/tsconfig.json deleted file mode 100644 index ea9d0cd..0000000 --- a/samples/SimpleFullStack/React/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "files": [], - "references": [ - { - "path": "./tsconfig.app.json" - }, - { - "path": "./tsconfig.node.json" - } - ] -} diff --git a/samples/SimpleFullStack/React/tsconfig.node.json b/samples/SimpleFullStack/React/tsconfig.node.json deleted file mode 100644 index 3afdd6e..0000000 --- a/samples/SimpleFullStack/React/tsconfig.node.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "composite": true, - "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "skipLibCheck": true, - "module": "ESNext", - "moduleResolution": "bundler", - "allowSyntheticDefaultImports": true, - "strict": true, - "noEmit": true - }, - "include": ["vite.config.ts"] -} diff --git a/samples/SimpleFullStack/React/tsconfig.test.json b/samples/SimpleFullStack/React/tsconfig.test.json deleted file mode 100644 index b12ff65..0000000 --- a/samples/SimpleFullStack/React/tsconfig.test.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.app.json", - "compilerOptions": { - "types": ["vitest", "node", "react", "react-dom"] - }, - "include": ["src/**/*.test.ts", "src/**/*.test.tsx"] -} \ No newline at end of file diff --git a/samples/SimpleFullStack/React/vite.config.ts b/samples/SimpleFullStack/React/vite.config.ts deleted file mode 100644 index 8bb3aa8..0000000 --- a/samples/SimpleFullStack/React/vite.config.ts +++ /dev/null @@ -1,27 +0,0 @@ -import react from '@vitejs/plugin-react'; -import path from 'path'; -import { defineConfig } from 'vitest/config'; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - test: { - environment: 'jsdom', - globals: true, // Enables globals like `describe`, `it`, and `expect` - }, - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - assets: path.resolve(__dirname, './src/assets'), - components: path.resolve(__dirname, './src/components'), - config: path.resolve(__dirname, './src/config'), - constants: path.resolve(__dirname, './src/constants'), - forms: path.resolve(__dirname, './src/forms'), - helpers: path.resolve(__dirname, './src/helpers'), - pages: path.resolve(__dirname, './src/pages'), - service: path.resolve(__dirname, './src/service'), - store: path.resolve(__dirname, './src/store'), - types: path.resolve(__dirname, './src/types'), - }, - }, -}); diff --git a/samples/SimpleFullStack/Web/.example.env b/samples/SimpleFullStack/Web/.example.env new file mode 100644 index 0000000..bb9149c --- /dev/null +++ b/samples/SimpleFullStack/Web/.example.env @@ -0,0 +1 @@ +VITE_BASE_URL=http://localhost:7071/api diff --git a/samples/SimpleFullStack/Web/.gitignore b/samples/SimpleFullStack/Web/.gitignore new file mode 100644 index 0000000..01774db --- /dev/null +++ b/samples/SimpleFullStack/Web/.gitignore @@ -0,0 +1,99 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TypeScript output +dist +out + +# Azure Functions artifacts +bin +obj +appsettings.json +local.settings.json + +# Azurite artifacts +__blobstorage__ +__queuestorage__ +__azurite_db*__.json \ No newline at end of file diff --git a/samples/SimpleFullStack/Web/README.md b/samples/SimpleFullStack/Web/README.md new file mode 100644 index 0000000..6762c9a --- /dev/null +++ b/samples/SimpleFullStack/Web/README.md @@ -0,0 +1,132 @@ +# Derek Huynen Personal Website + +This project is a modern, responsive personal website built with the following technologies: + +- **React** (with Vite) +- **TypeScript** +- **Material UI (MUI)** for UI components and theming +- **Zustand** for state management +- **Axios** for HTTP requests +- **React Hook Form** for form management +- **React Router DOM** for client-side routing +- **React Query** for data fetching and caching +- **Dynamic Icon Service** for consistent icon rendering +- **JSON-driven Content** for skills and projects + +## Project Structure + +``` +DerekHuynenWeb/ +├── public/ # Static assets +├── src/ +│ ├── App.tsx # Main app component +│ ├── main.tsx # Entry point +│ ├── assets/ # Images and icons +│ ├── config/ # App configuration +│ │ ├── json/ # JSON data for skills, projects, experience +│ ├── services/ # API and data services (Axios, React Query) +│ ├── store/ # Zustand state management +│ └── ui/ +│ ├── components/ # Reusable UI components +│ │ ├── icon_service/ # Centralized icon mapping and service +│ │ ├── infinite_icon_carousel/ # Animated skill carousel +│ │ ├── tech/ # TechChip and tech icon mapping +│ ├── forms/ # Form components (React Hook Form) +│ ├── nav/ # Navigation components +│ └── pages/ # Route pages +│ ├── blog/ # Blog-related pages +│ ├── home/ # Home page (hero, skills, featured projects) +│ └── projects/ # Projects showcase +├── package.json # Project dependencies +├── tsconfig.json # TypeScript configuration +├── vite.config.ts # Vite configuration +└── README.md # Project overview (this file) +``` + +## Data-Driven Features + +- **Skills Carousel:** Powered by `src/config/json/featuredSkillIconKeys.json` for easy updates and consistency +- **Featured Projects:** Powered by `src/config/json/featuredProjects.json` for maintainable, type-safe project data +- **Icon Service:** All icons (skills, nav, tech chips) are rendered via a central service for consistency and theme support + +## Planned Features + +- **Home Page:** Introduction, profile, and featured content +- **Projects Page:** Portfolio of personal and professional projects +- **Blog Page:** Articles, tutorials, and updates +- **Contact Form:** Reach out via email (with validation) +- **Responsive Design:** Mobile-friendly and accessible +- **Modern UI:** Material UI theming and components +- **State Management:** Global state with Zustand +- **Data Fetching:** React Query for async data +- **JSON Content:** Easily update skills and projects via JSON files + +## Getting Started + +1. Install dependencies: + ```powershell + npm install + ``` +2. Start the development server: + ```powershell + npm run dev + ``` + +--- + +Let's start scaffolding the app together! + +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config({ + extends: [ + // Remove ...tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}); +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x'; +import reactDom from 'eslint-plugin-react-dom'; + +export default tseslint.config({ + plugins: { + // Add the react-x and react-dom plugins + 'react-x': reactX, + 'react-dom': reactDom, + }, + rules: { + // other rules... + // Enable its recommended typescript rules + ...reactX.configs['recommended-typescript'].rules, + ...reactDom.configs.recommended.rules, + }, +}); +``` diff --git a/samples/SimpleFullStack/Web/eslint.config.js b/samples/SimpleFullStack/Web/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/samples/SimpleFullStack/Web/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/samples/SimpleFullStack/Web/index.html b/samples/SimpleFullStack/Web/index.html new file mode 100644 index 0000000..4faf546 --- /dev/null +++ b/samples/SimpleFullStack/Web/index.html @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + GitHub Copilot Example Project + + +
+ + + diff --git a/samples/SimpleFullStack/Web/package-lock.json b/samples/SimpleFullStack/Web/package-lock.json new file mode 100644 index 0000000..a8e4992 --- /dev/null +++ b/samples/SimpleFullStack/Web/package-lock.json @@ -0,0 +1,6306 @@ +{ + "name": "web", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@hookform/resolvers": "^5.0.1", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@tanstack/react-query": "^5.76.1", + "axios": "^1.9.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hook-form": "^7.56.4", + "react-icons": "^5.5.0", + "react-router-dom": "^7.6.0", + "react-vertical-timeline-component": "^3.5.3", + "zod": "^3.25.27", + "zustand": "^5.0.4" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/node": "^22.15.21", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@types/react-vertical-timeline-component": "^3.3.6", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.30.1", + "vite": "^6.3.5" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.2.tgz", + "integrity": "sha512-TUtMJYRPyUb/9aU8f3K0mjmjf6M9N5Woshn2CS6nqJSeJtTtQcpLUXjGt9vbF8ZGff0El99sWkLgzwW3VXnxZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-beta.53.tgz", + "integrity": "sha512-HVsEm3wjSe3BCXWxnyqrTWWQAxvtHR35F4q84jS68aS8R3WfbOnFEwlqsrWX5quZL0ArR68REOWRDCyG+JBSlQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-annotate-as-pure/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-call-delegate": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.0.0-beta.53.tgz", + "integrity": "sha512-kOdnk7nDkQsAM+fxhiN6sy0jNep5VN6jv7H8pbu2trW5ziopw+cwNxTkihLUAEC+gJU45WngJTZtjUMR/2Kckg==", + "license": "MIT", + "dependencies": { + "@babel/helper-hoist-variables": "7.0.0-beta.53", + "@babel/traverse": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/generator": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.53.tgz", + "integrity": "sha512-XnfdZ6oFVC4cE4+7jbEa1MLFSXrGY/SfSE6onUyyPSrRbjYs9sdrYKi/JgKGSJX65A8GFswHwWcBPCynfVEr5g==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53", + "jsesc": "^2.5.1", + "lodash": "^4.17.5", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/traverse": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.53.tgz", + "integrity": "sha512-JZh3vX/9ox9aoub2gLlpPRm8LM0yJuqzmp5MrbwD57SPh1dHMDWjGen9exbaITAe03t9MJV5PAacv0K2UJBffg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/generator": "7.0.0-beta.53", + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "debug": "^3.1.0", + "globals": "^11.1.0", + "invariant": "^2.2.0", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-call-delegate/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-call-delegate/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-call-delegate/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-define-map": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.0.0-beta.53.tgz", + "integrity": "sha512-rqDAadUz9cLul+epYez/X6PPwS85j/xL2q61JT14MFJaaCFKmQ8QhmZmktcSsYC8XhsDlaLAdwxSIw1a8oih9g==", + "license": "MIT", + "dependencies": { + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-define-map/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.53.tgz", + "integrity": "sha512-vmdaNg17OWa0lFVJqZLQcvc59KIOcJDpyvqr3EJT9BYsjh/JxDlYq/JpBzLpWv9AkXeBdY4NevZXD37gdsLu0Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-get-function-arity": "7.0.0-beta.53", + "@babel/template": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.53.tgz", + "integrity": "sha512-MCZLPfGfNBHdE5wNfY5eK1hpY3fyq8zq+NfbfFCUtIzHl7SfUzHzH8rKPBXSB2Ypetq2sBHdDyslSSgnG0Watg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-function-name/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-function-name/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-function-name/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-function-name/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-function-name/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-function-name/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.53.tgz", + "integrity": "sha512-jLbME3MfCVT88GLuUDJ1X+ErDeWi59aeBb/O6pyhp5C+eVRRiLxzptRmpvJqG+Va6aOBWSoJ8uBNKJ1ghT/ONg==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-get-function-arity/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0-beta.53.tgz", + "integrity": "sha512-ktLEBpVZkvPUjNn8JK11m/74cWw9H9U3QizAiJUPdnvkvz/F0ucMyIOpMa7vuhmpHmlRzgAraIlTrmQfxv6BGg==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-hoist-variables/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0-beta.53.tgz", + "integrity": "sha512-/rJvy0+ipYwZh5pXSrifbUo7Ct+Dfm85AQqSYphbX67qEOEk92phxE95Tpw1wtLgWEbWBQ3WRHfTyEadqlPocg==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-member-expression-to-functions/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0-beta.53.tgz", + "integrity": "sha512-B6DMEnC9slZtBDRRjLi7OTcfmsXPPZsRLldqQ0TZjWj4QuZWFSDlonVWIYI+2Fb9DiA/dZMXMv9JDgVGGibMkw==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-optimise-call-expression/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-regex": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.0.0-beta.53.tgz", + "integrity": "sha512-Wh9ORGs15i37YovmEcS2W8PQDMR9T5UxAL1EtHW/uAfqH4T703dRK/7rsREYjA2lQ8pPHJWN/y0tMuCoOvl3jQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.0.0-beta.53.tgz", + "integrity": "sha512-/KG2wmojlGgtuco35Aq5RYftukhXiql4dG7ux+oAnpi6wALb6BjPmUWJe5m57lNNQgLtCfQ8596j0h5GZu65QA==", + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "7.0.0-beta.53", + "@babel/helper-optimise-call-expression": "7.0.0-beta.53", + "@babel/traverse": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/generator": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.53.tgz", + "integrity": "sha512-XnfdZ6oFVC4cE4+7jbEa1MLFSXrGY/SfSE6onUyyPSrRbjYs9sdrYKi/JgKGSJX65A8GFswHwWcBPCynfVEr5g==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53", + "jsesc": "^2.5.1", + "lodash": "^4.17.5", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/traverse": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.53.tgz", + "integrity": "sha512-JZh3vX/9ox9aoub2gLlpPRm8LM0yJuqzmp5MrbwD57SPh1dHMDWjGen9exbaITAe03t9MJV5PAacv0K2UJBffg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/generator": "7.0.0-beta.53", + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "debug": "^3.1.0", + "globals": "^11.1.0", + "invariant": "^2.2.0", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-replace-supers/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-replace-supers/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-replace-supers/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.0.0-beta.53.tgz", + "integrity": "sha512-mq4csKX0vucrhZKgTG/ogNCuq6KiLEVXRDG5sRWggpuN4N6f/z+CyGNi83tqLRv9VLjV7IEQu/6UyI2wAUxFOg==", + "license": "MIT", + "dependencies": { + "@babel/template": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/template": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.53.tgz", + "integrity": "sha512-MCZLPfGfNBHdE5wNfY5eK1hpY3fyq8zq+NfbfFCUtIzHl7SfUzHzH8rKPBXSB2Ypetq2sBHdDyslSSgnG0Watg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/@babel/helper-simple-access/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-simple-access/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/@babel/helper-simple-access/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.53.tgz", + "integrity": "sha512-7twjNOXZFIuiGpfkaf2j1WuGFbfrmHS5ES9GXXXT0xbQ5UmyX9nvaTJHMt11t6pvIjv1xvtBVuDyMCrvyd+E/w==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53" + } + }, + "node_modules/@babel/helper-split-export-declaration/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.56.tgz", + "integrity": "sha512-q4TfI+jJISul6vVpZJktzH4tupwRiVk6KXRhB8PHqJ7erl966I6ePDXl9mAbE8jMM7YswhnnB0j1SYP7LBVyhg==", + "license": "MIT", + "peer": true, + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT", + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT", + "peer": true + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz", + "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz", + "integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.0.tgz", + "integrity": "sha512-E0OqhZv548Qdc0PwWhLVA2zmjJZSTvaL4ZhoswmI8NJEC1tpW2js6LLP827jrW9MEiXYdz3QS6+hask83w74yQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.0.tgz", + "integrity": "sha512-1mUPMAZ+Qk3jfgL5ftRR06ATH/Esi0izHl1z56H+df6cwIlCWG66RXciUqeJCttbOXOQ5y2DCjLZI/4t3Yg3LA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.0.tgz", + "integrity": "sha512-ahUJdrhEv+mCp4XHW+tHIEYzZMSRLg8z4AjUOsj44QpD1ZaMxQoVOG2xiHvLFdcsIPbgSRx1bg1eQSheHBgvtg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/core-downloads-tracker": "^7.1.0", + "@mui/system": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.1.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.0.tgz", + "integrity": "sha512-4Kck4jxhqF6YxNwJdSae1WgDfXVg0lIH6JVJ7gtuFfuKcQCgomJxPvUEOySTFRPz1IZzwz5OAcToskRdffElDA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/utils": "^7.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.0.tgz", + "integrity": "sha512-m0mJ0c6iRC+f9hMeRe0W7zZX1wme3oUX0+XTVHjPG7DJz6OdQ6K/ggEOq7ZdwilcpdsDUwwMfOmvO71qDkYd2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.0.tgz", + "integrity": "sha512-iedAWgRJMCxeMHvkEhsDlbvkK+qKf9me6ofsf7twk/jfT4P1ImVf7Rwb5VubEA0sikrVL+1SkoZM41M4+LNAVA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/private-theming": "^7.1.0", + "@mui/styled-engine": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz", + "integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz", + "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/types": "^7.4.2", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz", + "integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz", + "integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz", + "integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz", + "integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz", + "integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz", + "integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz", + "integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz", + "integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz", + "integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz", + "integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz", + "integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz", + "integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz", + "integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz", + "integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz", + "integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz", + "integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz", + "integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz", + "integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz", + "integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz", + "integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tanstack/query-core": { + "version": "5.76.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.76.0.tgz", + "integrity": "sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.76.1.tgz", + "integrity": "sha512-YxdLZVGN4QkT5YT1HKZQWiIlcgauIXEIsMOTSjvyD5wLYK8YVvKZUPAysMqossFJJfDpJW3pFn7WNZuPOqq+fw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.76.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", + "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", + "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-vertical-timeline-component": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@types/react-vertical-timeline-component/-/react-vertical-timeline-component-3.3.6.tgz", + "integrity": "sha512-OUvyPXRjXvUD/SNLO0CW0GbIxVF32Ios5qHecMSfw6kxnK1cPULD9NV80EuqZ3WmS/s6BgbcwmN8k4ISb3akhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.1.tgz", + "integrity": "sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", + "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001716", + "electron-to-chromium": "^1.5.149", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001718", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz", + "integrity": "sha512-AflseV1ahcSunK53NfEs9gFWgOEmzr0f+kaMFA4xiLZlr9Hzt7HxcSpIFcnNCUkz6R6dWKa54rUz3HUmI3nVcw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.155", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", + "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "dev": true, + "license": "ISC" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.1.0.tgz", + "integrity": "sha512-aibexHNbb/jiUSObBgpHLj+sIuUmJnYcgXBlrfsiDZ9rt4aF2TFRbyLgZ2iFQuVZ1K5Mx3FVkbKRSgKrbK3K2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-hook-form": { + "version": "7.56.4", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.4.tgz", + "integrity": "sha512-Rob7Ftz2vyZ/ZGsQZPaRdIefkgOSrQSPXfqBdvOPwJfoGnjwRJUs7EM7Kc1mcoDv3NOtqBzPGbcMB8CGn9CKgw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz", + "integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.0.tgz", + "integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==", + "license": "MIT", + "dependencies": { + "react-router": "7.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/react-vertical-timeline-component": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/react-vertical-timeline-component/-/react-vertical-timeline-component-3.5.3.tgz", + "integrity": "sha512-GS3kcppKpxbgOJDbPWsO3B6/MktVuo/YzebrOHcf5IF9VfOGeKysJuMyuKkvp6XDWAm0n1wlIYyYK1GxiGXSqQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@babel/preset-es2015": "^7.0.0-beta.53", + "classnames": "^2.2.6", + "react-intersection-observer": "^8.26.2" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.56.tgz", + "integrity": "sha512-OBeGs8UXWpKl0oK2T5nUXNl2yu8RKxqL/7aUnMtKDXCU6VUrNP3npdrPivBA11HPB15TVI49nWf2lntTzoUuAg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/highlight": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/core": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.0.0-beta.56.tgz", + "integrity": "sha512-IsytpdHZqo5pgJj4FTcpEMKmfXK9TdvThLZo4yUOjbuVZCy8NAwoeBnojvKCNf+139L7xNIIosp3RVA0cMkbOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.56", + "@babel/generator": "7.0.0-beta.56", + "@babel/helpers": "7.0.0-beta.56", + "@babel/parser": "7.0.0-beta.56", + "@babel/template": "7.0.0-beta.56", + "@babel/traverse": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56", + "convert-source-map": "^1.1.0", + "debug": "^3.1.0", + "json5": "^0.5.0", + "lodash": "^4.17.10", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/generator": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.56.tgz", + "integrity": "sha512-d+Ls/Vr5OU5FBDYQToXSqAluI3r2UaSoNZ41zD3sxdoVoaT8K5Bdh4So4eG4o//INGM7actValXGfb+5J1+r8w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "7.0.0-beta.56", + "jsesc": "^2.5.1", + "lodash": "^4.17.10", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-imports": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.53.tgz", + "integrity": "sha512-nyyERQH7kRCy0OR2Ek0+sD+wxZEhCmaLAVE7SylPYmCce1Dq8XGmibT1eQVekRkr78utXnDKMe4A269SBVlIRA==", + "license": "MIT", + "dependencies": { + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-imports/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0-beta.53.tgz", + "integrity": "sha512-FQrR3poCdkIxIl+QGkw9Fq3fYcEmcFloO/CSX26FYZuXcHZ5FbPLZajtdcQmPNWWqIicHzCXd0h+gkcRSP9siQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "7.0.0-beta.53", + "@babel/helper-simple-access": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "@babel/template": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/code-frame": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.53.tgz", + "integrity": "sha512-6o6EnDfG+zQqfrYDLPc5kGp6+klZFFFqGucljRcUa7IZuTBpvALWG0O+7rtOGFF1sYhr4jBib995RvFuNFxDMw==", + "license": "MIT", + "dependencies": { + "@babel/highlight": "7.0.0-beta.53" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/highlight": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.53.tgz", + "integrity": "sha512-5wvZd8RHAOzmTJ5bpupKM6x5OWXlViUK5ACDAUn7YXDd/JqQQZXi0CxDb8pH5IFV79mt6r5A/bZ/+NLhxpcZ5g==", + "license": "MIT", + "dependencies": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^3.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/parser": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.53.tgz", + "integrity": "sha512-SYoyLjcE+D28Ly2kkPXP6eIVy4YwViRSffri5WHi8PRxy8ngnx6mTXFzGAsSSPzUN3DK+sf8qBsdDGeQz1SJEw==", + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/template": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.53.tgz", + "integrity": "sha512-MCZLPfGfNBHdE5wNfY5eK1hpY3fyq8zq+NfbfFCUtIzHl7SfUzHzH8rKPBXSB2Ypetq2sBHdDyslSSgnG0Watg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.0.0-beta.53", + "@babel/parser": "7.0.0-beta.53", + "@babel/types": "7.0.0-beta.53", + "lodash": "^4.17.5" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-module-transforms/node_modules/@babel/types": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.53.tgz", + "integrity": "sha512-iL3DSWjQ890rA97uR5F1PhGtYniVGjqaRoRZtLz76bZhNNqmALftafrUnuJNzWC9z0eoaNcAtk7ZT/26mW/6Tg==", + "license": "MIT", + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.5", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helper-plugin-utils": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0-beta.53.tgz", + "integrity": "sha512-ziTIzKm3Hj8LvmV6HwyPC2t2NgSNg2T72Cifqaw3zo44ATRUeNI/nH7NoQZChNNwye97pbzs+UAHq6fCTt3uFg==", + "license": "MIT" + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/helpers": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.0.0-beta.56.tgz", + "integrity": "sha512-KaNBuVlAGW6sFCEWjliN29dL8K4L/ff8ZUaR/D5ou/JsqOuxLRy34Rf8WXMru3Et2g4Czly6vJeSmaYyf3s0lA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "7.0.0-beta.56", + "@babel/traverse": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/parser": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.0.0-beta.56.tgz", + "integrity": "sha512-JM0ughhbo+sPXw2Z+SUyowfYrAOhjanzjMshcLswBdXVelJCOeEKe/FqMqPWGVPQr7wByongXIn+MKdCpY7DBw==", + "license": "MIT", + "peer": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0-beta.53.tgz", + "integrity": "sha512-CxYIFD+eutA6WzT16fsxGn8Y1A50iG3JVOeL8MGn51h42E2ea5xvfWnT/aAthEuKqbHR+FDyxBZ7o2lXRMwAag==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0-beta.53.tgz", + "integrity": "sha512-bBCniYwYf4HI9jaVT1sf272aijJBDg17hAAAa1XduB/humwRD+XE5nZXYfh5ktMTjxHyWxB55LK6Y1W+F4RSDg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0-beta.53.tgz", + "integrity": "sha512-lBDSm853wqigbxRR9m71Ow91whK/gjOJzFo2kFwJulUZJaV/pdQGSXopANPRjITplKbDufIbClPdIK1V6dnN+w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "lodash": "^4.17.5" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-classes": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.0.0-beta.53.tgz", + "integrity": "sha512-Mv/3NVDJ40aawBuwg+Yy776Qynmo8FRM4RLtGk+TyIH9PKw83b1jL0Gxa1OvzXjBiizq6oQLOhUvWnmh1uSL5A==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "7.0.0-beta.53", + "@babel/helper-define-map": "7.0.0-beta.53", + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-optimise-call-expression": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-replace-supers": "7.0.0-beta.53", + "@babel/helper-split-export-declaration": "7.0.0-beta.53", + "globals": "^11.1.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0-beta.53.tgz", + "integrity": "sha512-5q7Epq0AIj3Pakj1w4WeJvcGZbPpIGymjXjinh7s/U8nCuXsxxjBiBXc0Hk0IaY95C51FxWmmbp3t1v7QqifNw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-destructuring": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.0.0-beta.53.tgz", + "integrity": "sha512-V7qWHCE5f2/hb4gd7rFe1MIkVoC0ZIo/XFR9YAHbRdUQtOI57FUjJL+Fz2BT5MaZYglfWFZy+YLnVzQhInJFJA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0-beta.53.tgz", + "integrity": "sha512-Laqs9gb/pkNVCZBugOYRhz8qaxz+XMz9CjKpF9eMrET1FToh0I2lp4yRKUHc4VPHd+oolkWvN55Hi28Ia3QOHQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-for-of": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0-beta.53.tgz", + "integrity": "sha512-7DD7gd/ywy3fTBZvQ8CiulD3SsUZLNrw22zD4nmH7LS8mTFcUnAsenbzHMDKmFs02ZwkLaAOS0lAQZFuSA/bNQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-function-name": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.0.0-beta.53.tgz", + "integrity": "sha512-mvkWR0ay5U8IZQiCRV02jnhk0uY+DHZCbmBVQ9KAYq0mvunmtxHsk3NtEzSTHMSyXr69BUkQlVnYlWtYN0HP6w==", + "license": "MIT", + "dependencies": { + "@babel/helper-function-name": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-instanceof": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-instanceof/-/plugin-transform-instanceof-7.0.0-beta.53.tgz", + "integrity": "sha512-/WX7rfrMy2nYfSGWdST3RZTPREDpNUNzl5YGx3O/M33qqepbeWBCwSggTSkdQ7iBAPi/CBjY7fGSVGCcCPR7/w==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-literals": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0-beta.53.tgz", + "integrity": "sha512-1bWy5iRSQngH9klvojOdMotFH9PWY6aRDWSiHddIsc54VQrKz9NH6bBAwhf+2Jt+SJfCUbAaGuleeZkfMPZ7fg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.0.0-beta.53.tgz", + "integrity": "sha512-DldT9fmUfjr+pY1/fLidnMq4wk2GiN4114oWshYHSd5Eachi5BkfM6Ao2CsTbDL5PyTy3rdIRB6K9nL+7ze0YQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.53.tgz", + "integrity": "sha512-PP1obXrhqknxHDtJ90DoB+SwjwLzC0bGCeYBAx1T0rgLjmWYMGB103wNpfdJ1jqUH5mmC6PTNKBlasTGiEc9Cw==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-simple-access": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0-beta.53.tgz", + "integrity": "sha512-ZCJZ18qEd7zf9F7Caph/z4TUARMlZiC5aI67wzOSeNpLhqSOrno5qxN7uukOuYvpsk3ji04S4tJOqHSuG+NUTA==", + "license": "MIT", + "dependencies": { + "@babel/helper-hoist-variables": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.0.0-beta.53.tgz", + "integrity": "sha512-TXX2R9rZQJbxyJ2leae4N+pT73t6Niolosa0WyZpLTGBmjKrFRHm/vpCm3v+tqb0GZMXbMQ/vzCRlMcQD8tPTQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-object-super": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.0.0-beta.53.tgz", + "integrity": "sha512-ko8FH0QnK76kZoNmP0KuZPFRlp2D07oLEXFylwpfOw6v2xmwqxajGiL51qrf0fhS5CT7zFbkHDmZljPfOo6Tzw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-replace-supers": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-parameters": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.0.0-beta.53.tgz", + "integrity": "sha512-X9zQrth/hIJ8EQFIF4lc+I16MmgEBoiog0izTC37wwXHEMtC9UWtspfDNVOIwuJ5vqKbQn8arqCUCh63SSG90g==", + "license": "MIT", + "dependencies": { + "@babel/helper-call-delegate": "7.0.0-beta.53", + "@babel/helper-get-function-arity": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-regenerator": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0-beta.53.tgz", + "integrity": "sha512-mGWykD0r9/7isJjTMG45kgUb63zWp9Rx1Mrd0tt8928IlNx4V2/1zjB1RqObiBE+ylkmhz6G3ywNmXuXSy9haQ==", + "license": "MIT", + "dependencies": { + "regenerator-transform": "^0.13.3" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0-beta.53.tgz", + "integrity": "sha512-kNjYrpDKi+ZYjWM1qQwD10ERplviaDb37/9RoLpeIRezf+DXPm5PMdIMYJH2gD552fbtkYwESp44hm2Izpi3rg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-spread": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0-beta.53.tgz", + "integrity": "sha512-pkK2dpGXiblw+OojZnyMdJAD/qUs0bDnhqqmN4WLdbvsbQW0wC/nWD7YmGUwl9B8kJ4cFmrvT1l5idh4d9Az3Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0-beta.53.tgz", + "integrity": "sha512-axZvAsF66i0/hBqtlAeVWB66OGx+EU/5cY4DEQtqd217LdbtrTpV3oynG0cZylNPeY2TCU848ojlBCA1L3PvTQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-regex": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-template-literals": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0-beta.53.tgz", + "integrity": "sha512-0q5OZuPVBAB/rsqulVLWT/bEoT1dEcKiVkyUagKgaVer2rXy1eB6eSFV3cJ/gpnlXDB2L0dCgeakgGJz/a1q4g==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "7.0.0-beta.53", + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0-beta.53.tgz", + "integrity": "sha512-xm9X4m0x+HrZ9r8GqNFjnFlip0nh7zUjGzGFOFD1l07gepng3tG1HYrO+LJ9WwOqnE1pqc8d0cCz7nvxlEqHLQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0-beta.53.tgz", + "integrity": "sha512-6DlspW3xuGi9JKzof3cqdel69TF/bE0tn7wC3tl1+VZ+BnUauEcZjlaN8azTD5YfgEUuiqWl9+Oz0WOyBf+0Yw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/helper-regex": "7.0.0-beta.53", + "regexpu-core": "^4.1.3" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/preset-es2015": { + "version": "7.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@babel/preset-es2015/-/preset-es2015-7.0.0-beta.53.tgz", + "integrity": "sha512-rcLuTFjJ4jlJdjkFeyX/BUyht3tGmfa3fgtAlPafNLLsAZ6nriJFhFNSXdB6Sl+seTcKVYvZoEFWNuVvqDXrnQ==", + "deprecated": "👋 We've deprecated any official yearly presets in 6.x in favor or babel-preset-env. For 7.x it would be @babel/preset-env.", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "7.0.0-beta.53", + "@babel/plugin-transform-arrow-functions": "7.0.0-beta.53", + "@babel/plugin-transform-block-scoped-functions": "7.0.0-beta.53", + "@babel/plugin-transform-block-scoping": "7.0.0-beta.53", + "@babel/plugin-transform-classes": "7.0.0-beta.53", + "@babel/plugin-transform-computed-properties": "7.0.0-beta.53", + "@babel/plugin-transform-destructuring": "7.0.0-beta.53", + "@babel/plugin-transform-duplicate-keys": "7.0.0-beta.53", + "@babel/plugin-transform-for-of": "7.0.0-beta.53", + "@babel/plugin-transform-function-name": "7.0.0-beta.53", + "@babel/plugin-transform-instanceof": "7.0.0-beta.53", + "@babel/plugin-transform-literals": "7.0.0-beta.53", + "@babel/plugin-transform-modules-amd": "7.0.0-beta.53", + "@babel/plugin-transform-modules-commonjs": "7.0.0-beta.53", + "@babel/plugin-transform-modules-systemjs": "7.0.0-beta.53", + "@babel/plugin-transform-modules-umd": "7.0.0-beta.53", + "@babel/plugin-transform-object-super": "7.0.0-beta.53", + "@babel/plugin-transform-parameters": "7.0.0-beta.53", + "@babel/plugin-transform-regenerator": "7.0.0-beta.53", + "@babel/plugin-transform-shorthand-properties": "7.0.0-beta.53", + "@babel/plugin-transform-spread": "7.0.0-beta.53", + "@babel/plugin-transform-sticky-regex": "7.0.0-beta.53", + "@babel/plugin-transform-template-literals": "7.0.0-beta.53", + "@babel/plugin-transform-typeof-symbol": "7.0.0-beta.53", + "@babel/plugin-transform-unicode-regex": "7.0.0-beta.53" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.50 <7.0.0-rc.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/template": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.56.tgz", + "integrity": "sha512-rsR9K18h0oiJTUmS/ICYREbV8qhPTic4SIqDSkzv9xOxupt7dKj8hWmZQLGPySO5x6cdn8py039o1wPQnsEGHg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.56", + "@babel/parser": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56", + "lodash": "^4.17.10" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.56.tgz", + "integrity": "sha512-9WTqtKP2Ll+jG68r+JEecXAbdH/kk5inN1VDSDaTgdYtZ82BYUS9XRWMVpc5HB9LJsu2ZEyUA1cGybID7eeOXA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "7.0.0-beta.56", + "@babel/generator": "7.0.0-beta.56", + "@babel/helper-function-name": "7.0.0-beta.56", + "@babel/helper-split-export-declaration": "7.0.0-beta.56", + "@babel/parser": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56", + "debug": "^3.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.10" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse/node_modules/@babel/helper-function-name": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.56.tgz", + "integrity": "sha512-Lq4nPOt1j3sUq+1GVrw57dKq6wBKAHplGjYzEG8dkytqo93i6uSKKKg3smYXx2qohEVD5ciAyJjgRJq7RQu4Lg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-get-function-arity": "7.0.0-beta.56", + "@babel/template": "7.0.0-beta.56", + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse/node_modules/@babel/helper-get-function-arity": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.56.tgz", + "integrity": "sha512-QU9EVlnDGTzBasgrdo/I4+RzZS7oqzz9YcetpYko3bp+VsRGokqsAQl3gIvxWTtxwibwboDEdBx+fGArtb2fhw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.56.tgz", + "integrity": "sha512-j886mQJQg5HDF7X0qK/AfNdrpIYUcJHxRKwBJ9dUvhpO3eFqsTLbJJpitgLaJQjh9D7Db5Aiq8MRghj3+MH57g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "7.0.0-beta.56" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/@babel/types": { + "version": "7.0.0-beta.56", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.56.tgz", + "integrity": "sha512-fRIBeHtKxAD3D1E7hYSpG4MnLt0AfzHHs5gfVclOB0NlfLu3qiWU/IqdbK2ixTK61424iEkV1P/VAzndx6ungA==", + "license": "MIT", + "peer": true, + "dependencies": { + "esutils": "^2.0.2", + "lodash": "^4.17.10", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/react-vertical-timeline-component/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==", + "license": "MIT" + }, + "node_modules/react-vertical-timeline-component/node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "license": "MIT", + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/react-intersection-observer": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-8.34.0.tgz", + "integrity": "sha512-TYKh52Zc0Uptp5/b4N91XydfSGKubEhgZRtcg1rhTKABXijc4Sdr1uTp5lJ8TN27jwUsdXxjHXtHa0kPj704sw==", + "license": "MIT", + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/react-vertical-timeline-component/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz", + "integrity": "sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-transform": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz", + "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==", + "license": "MIT", + "dependencies": { + "private": "^0.1.6" + } + }, + "node_modules/regexpu-core": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.8.0.tgz", + "integrity": "sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==", + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^9.0.0", + "regjsgen": "^0.5.2", + "regjsparser": "^0.7.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.7.0.tgz", + "integrity": "sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==", + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz", + "integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.41.0", + "@rollup/rollup-android-arm64": "4.41.0", + "@rollup/rollup-darwin-arm64": "4.41.0", + "@rollup/rollup-darwin-x64": "4.41.0", + "@rollup/rollup-freebsd-arm64": "4.41.0", + "@rollup/rollup-freebsd-x64": "4.41.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.41.0", + "@rollup/rollup-linux-arm-musleabihf": "4.41.0", + "@rollup/rollup-linux-arm64-gnu": "4.41.0", + "@rollup/rollup-linux-arm64-musl": "4.41.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.41.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-gnu": "4.41.0", + "@rollup/rollup-linux-riscv64-musl": "4.41.0", + "@rollup/rollup-linux-s390x-gnu": "4.41.0", + "@rollup/rollup-linux-x64-gnu": "4.41.0", + "@rollup/rollup-linux-x64-musl": "4.41.0", + "@rollup/rollup-win32-arm64-msvc": "4.41.0", + "@rollup/rollup-win32-ia32-msvc": "4.41.0", + "@rollup/rollup-win32-x64-msvc": "4.41.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.32.1.tgz", + "integrity": "sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.32.1", + "@typescript-eslint/parser": "8.32.1", + "@typescript-eslint/utils": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.27", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.27.tgz", + "integrity": "sha512-xkYsE+ztNLzBeoAG8Ipd2ICr86gyMpovQlB+Vid1LT7V16/Dj0z+Up1u1qxNX58cmJ/AtG2mvGw/7+jK48xEYw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.4.tgz", + "integrity": "sha512-39VFTN5InDtMd28ZhjLyuTnlytDr9HfwO512Ai4I8ZABCoyAj4F1+sr7sD1jP/+p7k77Iko0Pb5NhgBFDCX0kQ==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/samples/SimpleFullStack/Web/package.json b/samples/SimpleFullStack/Web/package.json new file mode 100644 index 0000000..cdefe39 --- /dev/null +++ b/samples/SimpleFullStack/Web/package.json @@ -0,0 +1,44 @@ +{ + "name": "web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@hookform/resolvers": "^5.0.1", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@tanstack/react-query": "^5.76.1", + "axios": "^1.9.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-hook-form": "^7.56.4", + "react-icons": "^5.5.0", + "react-router-dom": "^7.6.0", + "react-vertical-timeline-component": "^3.5.3", + "zod": "^3.25.27", + "zustand": "^5.0.4" + }, + "devDependencies": { + "@eslint/js": "^9.25.0", + "@types/node": "^22.15.21", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@types/react-vertical-timeline-component": "^3.3.6", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "typescript": "~5.8.3", + "typescript-eslint": "^8.30.1", + "vite": "^6.3.5" + } +} \ No newline at end of file diff --git a/samples/SimpleFullStack/Web/public/github-copilot-logo.svg b/samples/SimpleFullStack/Web/public/github-copilot-logo.svg new file mode 100644 index 0000000..6b60e46 --- /dev/null +++ b/samples/SimpleFullStack/Web/public/github-copilot-logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/samples/SimpleFullStack/Web/public/routes.json b/samples/SimpleFullStack/Web/public/routes.json new file mode 100644 index 0000000..f1bc58d --- /dev/null +++ b/samples/SimpleFullStack/Web/public/routes.json @@ -0,0 +1,9 @@ +{ + "routes": [ + { + "route": "/*", + "serve": "/index.html", + "statusCode": 200 + } + ] +} \ No newline at end of file diff --git a/samples/SimpleFullStack/Web/routes.json b/samples/SimpleFullStack/Web/routes.json new file mode 100644 index 0000000..f1bc58d --- /dev/null +++ b/samples/SimpleFullStack/Web/routes.json @@ -0,0 +1,9 @@ +{ + "routes": [ + { + "route": "/*", + "serve": "/index.html", + "statusCode": 200 + } + ] +} \ No newline at end of file diff --git a/samples/SimpleFullStack/Web/src/App.tsx b/samples/SimpleFullStack/Web/src/App.tsx new file mode 100644 index 0000000..314f281 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/App.tsx @@ -0,0 +1,33 @@ +import React, { Suspense } from 'react'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; +import { ThemeProvider, CssBaseline } from '@mui/material'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import myRoutes from 'config/routes.tsx'; +import GlobalModal from 'ui/components/global_modal.tsx/GlobalModal'; +import GlobalSnackbar from 'ui/components/global_snackbar/GlobalSnackbar'; +import ErrorBoundary from 'ui/components/boundaries/ErrorBoundary'; +import LoadingPage from 'ui/components/boundaries/LoadingPage'; +import { lightTheme } from 'config/theme/lightTheme'; + + +const queryClient = new QueryClient(); +const router = createBrowserRouter(myRoutes); + +const App: React.FC = () => { + return ( + + + + + + + }> + + + + + + ); +}; + +export default App; diff --git a/samples/SimpleFullStack/Web/src/config/constants.ts b/samples/SimpleFullStack/Web/src/config/constants.ts new file mode 100644 index 0000000..52695b3 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/config/constants.ts @@ -0,0 +1,25 @@ +import PersonIcon from '@mui/icons-material/Person'; +import ShoppingBasketIcon from '@mui/icons-material/ShoppingBasket'; + +export const APPLICATION = { + Home: { + id: 'home', + label: 'Home', + route: '/', + icon: PersonIcon, + }, + Products: { + id: 'products', + label: 'Products', + route: '/products', + icon: ShoppingBasketIcon, + }, +}; + +//Make Applicattion an array +export const APPLICATION_ARRAY = Object.values(APPLICATION); + +export type ApplicationType = (typeof APPLICATION)[keyof typeof APPLICATION]; + +export const USER_NAME = 'Neudesic'; +export const BASE_URL = import.meta.env.VITE_BASE_URL || ''; diff --git a/samples/SimpleFullStack/Web/src/config/routes.tsx b/samples/SimpleFullStack/Web/src/config/routes.tsx new file mode 100644 index 0000000..c210c3f --- /dev/null +++ b/samples/SimpleFullStack/Web/src/config/routes.tsx @@ -0,0 +1,38 @@ +import type { RouteObject } from 'react-router-dom'; +import MainLayout from 'ui/components/layouts/MainLayout'; +import { APPLICATION } from './constants'; +import NotFoundPage from 'ui/components/boundaries/NotFoundPage'; + +const myRoutes: RouteObject[] = [ + { + path: APPLICATION.Home.route, + element: , + children: [{ + path: APPLICATION.Home.route, + lazy: async () => { + const Component = await import('ui/pages/home/HomePage'); + return { Component: Component.default }; + }, + }, { + path: APPLICATION.Products.route, + lazy: async () => { + const Component = await import('ui/pages/product/ProductsPage'); + return { Component: Component.default }; + }, + }, + { + path: `${APPLICATION.Products.route}/:productId`, + lazy: async () => { + const Component = await import('ui/pages/product/ProductDetailPage'); + return { Component: Component.default }; + }, + }, + { + path: '*', + element: , + }, + ], + }, +]; + +export default myRoutes; diff --git a/samples/SimpleFullStack/Web/src/config/theme/lightTheme.ts b/samples/SimpleFullStack/Web/src/config/theme/lightTheme.ts new file mode 100644 index 0000000..4c1b178 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/config/theme/lightTheme.ts @@ -0,0 +1,30 @@ +import { createTheme } from '@mui/material/styles'; + +export const lightTheme = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#e06a00', // Slightly darker orange for button color + contrastText: '#FFFFFF', + }, + secondary: { + main: '#393E46', // Selected nav link color (orange) + contrastText: '#FFFFFF', + }, + background: { + default: '#E5E5E5', + paper: '#F2F2F2', + }, + text: { + primary: '#212121', + secondary: '#616161', + }, + error: { main: '#D32F2F', contrastText: '#FFFFFF' }, + warning: { main: '#F4802B', contrastText: '#FFFFFF' }, + success: { main: '#388E3C', contrastText: '#FFFFFF' }, + divider: '#E0E0E0', + }, + typography: { + fontFamily: 'Roboto, Arial, sans-serif', + }, +}); diff --git a/samples/SimpleFullStack/Web/src/json/categories.json b/samples/SimpleFullStack/Web/src/json/categories.json new file mode 100644 index 0000000..22ecdf5 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/json/categories.json @@ -0,0 +1,75 @@ +[ + { + "categoryID": 1, + "name": "Electronics", + "description": "Electronic devices and accessories", + "parentCategoryID": null, + "subCategories": [ + { + "categoryID": 2, + "name": "Computers", + "description": "Desktop and laptop computers", + "parentCategoryID": 1, + "subCategories": [ + { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + }, + { + "categoryID": 6, + "name": "Desktops", + "description": "Stationary computers", + "parentCategoryID": 2, + "subCategories": [] + } + ] + }, + { + "categoryID": 3, + "name": "Smartphones", + "description": "Mobile phones and accessories", + "parentCategoryID": 1, + "subCategories": [] + }, + { + "categoryID": 4, + "name": "Audio", + "description": "Headphones, speakers, and audio equipment", + "parentCategoryID": 1, + "subCategories": [] + } + ] + }, + { + "categoryID": 7, + "name": "Clothing", + "description": "Apparel and fashion items", + "parentCategoryID": null, + "subCategories": [ + { + "categoryID": 8, + "name": "Men's", + "description": "Men's clothing", + "parentCategoryID": 7, + "subCategories": [] + }, + { + "categoryID": 9, + "name": "Women's", + "description": "Women's clothing", + "parentCategoryID": 7, + "subCategories": [] + } + ] + }, + { + "categoryID": 10, + "name": "Home & Garden", + "description": "Items for home and outdoor spaces", + "parentCategoryID": null, + "subCategories": [] + } +] \ No newline at end of file diff --git a/samples/SimpleFullStack/Web/src/json/productAttributes.json b/samples/SimpleFullStack/Web/src/json/productAttributes.json new file mode 100644 index 0000000..2592693 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/json/productAttributes.json @@ -0,0 +1,230 @@ +[ + { + "attributeID": 1, + "productID": 1, + "attributeName": "Processor", + "attributeValue": "Apple M2 Pro" + }, + { + "attributeID": 2, + "productID": 1, + "attributeName": "RAM", + "attributeValue": "16GB" + }, + { + "attributeID": 3, + "productID": 1, + "attributeName": "Storage", + "attributeValue": "512GB SSD" + }, + { + "attributeID": 4, + "productID": 1, + "attributeName": "Screen Size", + "attributeValue": "16 inches" + }, + { + "attributeID": 5, + "productID": 1, + "attributeName": "Color", + "attributeValue": "Space Gray" + }, + { + "attributeID": 6, + "productID": 2, + "attributeName": "Processor", + "attributeValue": "Intel Core i7-13700H" + }, + { + "attributeID": 7, + "productID": 2, + "attributeName": "RAM", + "attributeValue": "16GB" + }, + { + "attributeID": 8, + "productID": 2, + "attributeName": "Storage", + "attributeValue": "1TB SSD" + }, + { + "attributeID": 9, + "productID": 2, + "attributeName": "Screen Size", + "attributeValue": "15.6 inches" + }, + { + "attributeID": 10, + "productID": 2, + "attributeName": "Color", + "attributeValue": "Silver" + }, + { + "attributeID": 11, + "productID": 3, + "attributeName": "Storage", + "attributeValue": "256GB" + }, + { + "attributeID": 12, + "productID": 3, + "attributeName": "Color", + "attributeValue": "Titanium" + }, + { + "attributeID": 13, + "productID": 3, + "attributeName": "Screen Size", + "attributeValue": "6.1 inches" + }, + { + "attributeID": 14, + "productID": 3, + "attributeName": "Camera", + "attributeValue": "48MP Triple Camera" + }, + { + "attributeID": 15, + "productID": 4, + "attributeName": "Storage", + "attributeValue": "512GB" + }, + { + "attributeID": 16, + "productID": 4, + "attributeName": "Color", + "attributeValue": "Phantom Black" + }, + { + "attributeID": 17, + "productID": 4, + "attributeName": "Screen Size", + "attributeValue": "6.8 inches" + }, + { + "attributeID": 18, + "productID": 4, + "attributeName": "Camera", + "attributeValue": "200MP Quad Camera" + }, + { + "attributeID": 19, + "productID": 5, + "attributeName": "Color", + "attributeValue": "Black" + }, + { + "attributeID": 20, + "productID": 5, + "attributeName": "Battery Life", + "attributeValue": "30 hours" + }, + { + "attributeID": 21, + "productID": 5, + "attributeName": "Noise Cancellation", + "attributeValue": "Active" + }, + { + "attributeID": 22, + "productID": 6, + "attributeName": "Processor", + "attributeValue": "Intel Core i5-13400" + }, + { + "attributeID": 23, + "productID": 6, + "attributeName": "RAM", + "attributeValue": "8GB" + }, + { + "attributeID": 24, + "productID": 6, + "attributeName": "Storage", + "attributeValue": "512GB SSD" + }, + { + "attributeID": 25, + "productID": 7, + "attributeName": "Size", + "attributeValue": "Large" + }, + { + "attributeID": 26, + "productID": 7, + "attributeName": "Material", + "attributeValue": "100% Cotton" + }, + { + "attributeID": 27, + "productID": 7, + "attributeName": "Color", + "attributeValue": "Navy Blue" + }, + { + "attributeID": 28, + "productID": 8, + "attributeName": "Size", + "attributeValue": "Medium" + }, + { + "attributeID": 29, + "productID": 8, + "attributeName": "Material", + "attributeValue": "Cotton Blend" + }, + { + "attributeID": 30, + "productID": 8, + "attributeName": "Color", + "attributeValue": "Floral Print" + }, + { + "attributeID": 31, + "productID": 9, + "attributeName": "Plants Included", + "attributeValue": "Snake Plant, Pothos, ZZ Plant" + }, + { + "attributeID": 32, + "productID": 9, + "attributeName": "Pot Material", + "attributeValue": "Ceramic" + }, + { + "attributeID": 33, + "productID": 9, + "attributeName": "Care Level", + "attributeValue": "Easy" + }, + { + "attributeID": 34, + "productID": 10, + "attributeName": "Processor", + "attributeValue": "Intel Core i9-13900H" + }, + { + "attributeID": 35, + "productID": 10, + "attributeName": "Graphics", + "attributeValue": "NVIDIA RTX 4070 8GB" + }, + { + "attributeID": 36, + "productID": 10, + "attributeName": "RAM", + "attributeValue": "32GB DDR5" + }, + { + "attributeID": 37, + "productID": 10, + "attributeName": "Storage", + "attributeValue": "1TB NVMe SSD" + }, + { + "attributeID": 38, + "productID": 10, + "attributeName": "Screen", + "attributeValue": "17.3 inch 240Hz QHD" + } +] \ No newline at end of file diff --git a/samples/SimpleFullStack/Web/src/json/products.json b/samples/SimpleFullStack/Web/src/json/products.json new file mode 100644 index 0000000..11f8b09 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/json/products.json @@ -0,0 +1,162 @@ +[ + { + "productID": 1, + "name": "MacBook Pro 16-inch", + "description": "Apple MacBook Pro with M2 Pro chip, 16GB RAM, 512GB SSD", + "sku": "APP-MBP16-M2P", + "categoryID": 5, + "brand": "Apple", + "isActive": true, + "category": { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + } + }, + { + "productID": 2, + "name": "Dell XPS 15", + "description": "Dell XPS 15 with Intel i7, 16GB RAM, 1TB SSD", + "sku": "DEL-XPS15-I7", + "categoryID": 5, + "brand": "Dell", + "isActive": true, + "category": { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + } + }, + { + "productID": 3, + "name": "iPhone 15 Pro", + "description": "Apple iPhone 15 Pro, 256GB", + "sku": "APP-IP15P-256", + "categoryID": 3, + "brand": "Apple", + "isActive": true, + "category": { + "categoryID": 3, + "name": "Smartphones", + "description": "Mobile phones and accessories", + "parentCategoryID": 1, + "subCategories": [] + } + }, + { + "productID": 4, + "name": "Samsung Galaxy S24 Ultra", + "description": "Samsung Galaxy S24 Ultra, 512GB", + "sku": "SAM-GS24U-512", + "categoryID": 3, + "brand": "Samsung", + "isActive": true, + "category": { + "categoryID": 3, + "name": "Smartphones", + "description": "Mobile phones and accessories", + "parentCategoryID": 1, + "subCategories": [] + } + }, + { + "productID": 5, + "name": "Sony WH-1000XM5", + "description": "Sony Wireless Noise Cancelling Headphones", + "sku": "SON-WH1000XM5", + "categoryID": 4, + "brand": "Sony", + "isActive": true, + "category": { + "categoryID": 4, + "name": "Audio", + "description": "Headphones, speakers, and audio equipment", + "parentCategoryID": 1, + "subCategories": [] + } + }, + { + "productID": 6, + "name": "HP Pavilion Desktop", + "description": "HP Pavilion Desktop PC with Intel i5, 8GB RAM, 512GB SSD", + "sku": "HP-PVDTOP-I5", + "categoryID": 6, + "brand": "HP", + "isActive": true, + "category": { + "categoryID": 6, + "name": "Desktops", + "description": "Stationary computers", + "parentCategoryID": 2, + "subCategories": [] + } + }, + { + "productID": 7, + "name": "Men's Cotton T-Shirt", + "description": "Classic fit cotton t-shirt, various colors", + "sku": "CLO-MCTS-LG", + "categoryID": 8, + "brand": "Generic", + "isActive": true, + "category": { + "categoryID": 8, + "name": "Men's", + "description": "Men's clothing", + "parentCategoryID": 7, + "subCategories": [] + } + }, + { + "productID": 8, + "name": "Women's Casual Dress", + "description": "Summer casual dress, knee length", + "sku": "CLO-WCDR-MD", + "categoryID": 9, + "brand": "Generic", + "isActive": true, + "category": { + "categoryID": 9, + "name": "Women's", + "description": "Women's clothing", + "parentCategoryID": 7, + "subCategories": [] + } + }, + { + "productID": 9, + "name": "Indoor Plant Set", + "description": "Set of 3 small indoor plants with pots", + "sku": "HG-IPLTS-3PK", + "categoryID": 10, + "brand": "GreenThumb", + "isActive": true, + "category": { + "categoryID": 10, + "name": "Home & Garden", + "description": "Items for home and outdoor spaces", + "parentCategoryID": null, + "subCategories": [] + } + }, + { + "productID": 10, + "name": "Gaming Laptop", + "description": "High-performance gaming laptop with RTX 4070", + "sku": "MSI-GMLPT-4070", + "categoryID": 5, + "brand": "MSI", + "isActive": false, + "category": { + "categoryID": 5, + "name": "Laptops", + "description": "Portable computers", + "parentCategoryID": 2, + "subCategories": [] + } + } +] \ No newline at end of file diff --git a/samples/SimpleFullStack/Web/src/main.tsx b/samples/SimpleFullStack/Web/src/main.tsx new file mode 100644 index 0000000..4aff025 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/samples/SimpleFullStack/Web/src/services/axiosClient.ts b/samples/SimpleFullStack/Web/src/services/axiosClient.ts new file mode 100644 index 0000000..c2e0896 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/services/axiosClient.ts @@ -0,0 +1,13 @@ +import axios from 'axios'; +import { BASE_URL } from 'config/constants'; + +// Create a pre-configured Axios instance +const axiosClient = axios.create({ + baseURL: BASE_URL, // Set your API base URL here + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + }, +}); + +export default axiosClient; diff --git a/samples/SimpleFullStack/Web/src/store/useAppConfig.ts b/samples/SimpleFullStack/Web/src/store/useAppConfig.ts new file mode 100644 index 0000000..86b84e0 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/store/useAppConfig.ts @@ -0,0 +1,15 @@ +import { create } from 'zustand'; + +export type ThemeMode = 'light' | 'dark'; + +interface AppConfigStore { + theme: ThemeMode; + setTheme: (theme: ThemeMode) => void; +} + +const useAppConfig = create(set => ({ + theme: 'light', + setTheme: theme => set({ theme }), +})); + +export default useAppConfig; diff --git a/samples/SimpleFullStack/Web/src/types/Category.ts b/samples/SimpleFullStack/Web/src/types/Category.ts new file mode 100644 index 0000000..adc9f5b --- /dev/null +++ b/samples/SimpleFullStack/Web/src/types/Category.ts @@ -0,0 +1,17 @@ +export interface Category { + categoryID: number; + name: string; + description?: string; + parentCategoryID?: number; + subCategories?: Category[]; +} + +export interface AddCategory { + name: string; + description?: string; + parentCategoryID?: number; +} + +export interface UpdateCategoryDescription { + description?: string; +} diff --git a/samples/SimpleFullStack/Web/src/types/Product.ts b/samples/SimpleFullStack/Web/src/types/Product.ts new file mode 100644 index 0000000..525f165 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/types/Product.ts @@ -0,0 +1,30 @@ +import type { Category } from './Category'; + +export interface Product { + productID: number; + name: string; + description?: string; + sku: string; + categoryID: number; + brand?: string; + isActive: boolean; + category: Category; +} + +export interface AddProduct { + name: string; + description?: string; + sku: string; + categoryID: number; + brand?: string; + isActive: boolean; +} + +export interface UpdateProduct { + name?: string; + description?: string; + sku?: string; + categoryID?: number; + brand?: string; + isActive?: boolean; +} diff --git a/samples/SimpleFullStack/Web/src/types/ProductAttribute.ts b/samples/SimpleFullStack/Web/src/types/ProductAttribute.ts new file mode 100644 index 0000000..d9f8163 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/types/ProductAttribute.ts @@ -0,0 +1,17 @@ +export interface ProductAttribute { + attributeID: number; + productID: number; + attributeName: string; + attributeValue: string; +} + +export interface AddProductAttribute { + productID: number; + attributeName: string; + attributeValue: string; +} + +export interface UpdateProductAttribute { + attributeName: string; + attributeValue: string; +} diff --git a/samples/SimpleFullStack/Web/src/ui/components/boundaries/ErrorBoundary.tsx b/samples/SimpleFullStack/Web/src/ui/components/boundaries/ErrorBoundary.tsx new file mode 100644 index 0000000..08f55c2 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/boundaries/ErrorBoundary.tsx @@ -0,0 +1,51 @@ +import React, { Component, type ReactNode } from 'react'; +import { Box, Typography, Button } from '@mui/material'; + +interface ErrorBoundaryProps { + children: ReactNode; +} + +interface ErrorBoundaryState { + hasError: boolean; + error?: Error; +} + +class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: Error) { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + console.error('ErrorBoundary caught an error:', error, errorInfo); + } + + handleReload = () => { + window.location.reload(); + }; + + render() { + if (this.state.hasError) { + return ( + + + Something went wrong. + + + {this.state.error?.message || 'An unexpected error occurred.'} + + + + ); + } + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/samples/SimpleFullStack/Web/src/ui/components/boundaries/LoadingPage.tsx b/samples/SimpleFullStack/Web/src/ui/components/boundaries/LoadingPage.tsx new file mode 100644 index 0000000..e528de1 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/boundaries/LoadingPage.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Box, Typography, CircularProgress } from '@mui/material'; + +const LoadingPage: React.FC = () => ( + + + + Loading... + + +); + +export default LoadingPage; diff --git a/samples/SimpleFullStack/Web/src/ui/components/boundaries/NotFoundPage.tsx b/samples/SimpleFullStack/Web/src/ui/components/boundaries/NotFoundPage.tsx new file mode 100644 index 0000000..21cbc90 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/boundaries/NotFoundPage.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { Box, Typography } from '@mui/material'; + +const NotFoundPage: React.FC = () => ( + + + 404 + + + Page Not Found + + + Sorry, the page you are looking for does not exist. + + +); + +export default NotFoundPage; diff --git a/samples/SimpleFullStack/Web/src/ui/components/bread_crumb/BreadCrumb.tsx b/samples/SimpleFullStack/Web/src/ui/components/bread_crumb/BreadCrumb.tsx new file mode 100644 index 0000000..d2634f0 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/bread_crumb/BreadCrumb.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Breadcrumbs, capitalize, Link, Typography } from '@mui/material'; +import { useLocation, Link as RouterLink } from 'react-router-dom'; + +const BreadCrumb: React.FC = () => { + const location = useLocation(); + const pathnames = location.pathname.split('/').filter((x) => x); + if (pathnames.length === 0) return null; + return ( + + + Home + + {pathnames.map((value, index) => { + const to = `/${pathnames.slice(0, index + 1).join('/')}`; + const isLast = index === pathnames.length - 1; + return isLast ? ( + + {capitalize(decodeURIComponent(value))} + + ) : ( + + {capitalize(decodeURIComponent(value))} + + ); + })} + + ); +}; + +export default BreadCrumb; diff --git a/samples/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx b/samples/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx new file mode 100644 index 0000000..bf31798 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/footer/Footer.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Box } from '@mui/material'; + + +const Footer: React.FC = () => { + return ( + + + ); +}; + +export default Footer; diff --git a/samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/GlobalModal.tsx b/samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/GlobalModal.tsx new file mode 100644 index 0000000..1fc3aa6 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/GlobalModal.tsx @@ -0,0 +1,64 @@ +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import IconButton from '@mui/material/IconButton'; +import CloseIcon from '@mui/icons-material/Close'; +import useGlobalModal from './useGlobalModal'; + +const GlobalModal = () => { + const { isOpen, content, close, dialogProps, title, titleColor, primaryAction, primaryActionText, closeAction, formName } = useGlobalModal(); + + const handleClose = () => { + if (closeAction) { + closeAction(); + } else { + close(); + } + }; + + return ( + + {title && ( + + {title} + + + + + )} + {content} + + + + {formName && ( + + )} + {primaryAction && ( + + )} + + + + ); +}; + +export default GlobalModal; diff --git a/samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/useGlobalModal.tsx b/samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/useGlobalModal.tsx new file mode 100644 index 0000000..a463857 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/global_modal.tsx/useGlobalModal.tsx @@ -0,0 +1,64 @@ +import { create } from 'zustand'; +import type { DialogProps } from '@mui/material/Dialog'; +import type { ReactNode } from 'react'; + +export type GlobalModalStore = { + isOpen: boolean; + content: ReactNode | null; + dialogProps?: Omit; + title?: ReactNode; + titleColor?: string; + primaryAction?: () => void; + primaryActionText?: string; + closeAction?: () => void; + formName?: string; + open: ( + content: ReactNode, + options?: { + dialogProps?: GlobalModalStore['dialogProps']; + title?: ReactNode; + titleColor?: string; + primaryAction?: () => void; + primaryActionText?: string; + closeAction?: () => void; + formName?: string; + } + ) => void; + close: () => void; +}; + +const useGlobalModal = create(set => ({ + isOpen: false, + content: null, + dialogProps: undefined, + title: undefined, + titleColor: undefined, + primaryAction: undefined, + primaryActionText: undefined, + closeAction: undefined, + formName: undefined, + open: (content, options) => set({ + isOpen: true, + content, + dialogProps: options?.dialogProps, + title: options?.title, + titleColor: options?.titleColor, + primaryAction: options?.primaryAction, + primaryActionText: options?.primaryActionText, + closeAction: options?.closeAction, + formName: options?.formName, + }), + close: () => set({ + isOpen: false, + content: null, + dialogProps: undefined, + title: undefined, + titleColor: undefined, + primaryAction: undefined, + primaryActionText: undefined, + closeAction: undefined, + formName: undefined, + }), +})); + +export default useGlobalModal; diff --git a/samples/SimpleFullStack/Web/src/ui/components/global_snackbar/GlobalSnackbar.tsx b/samples/SimpleFullStack/Web/src/ui/components/global_snackbar/GlobalSnackbar.tsx new file mode 100644 index 0000000..f066073 --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/global_snackbar/GlobalSnackbar.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import Snackbar from '@mui/material/Snackbar'; +import Alert from '@mui/material/Alert'; +import Slide from '@mui/material/Slide'; +import type { SlideProps } from '@mui/material/Slide'; +import useGlobalSnackbar from './useGlobalSnackbar'; + +function TransitionUp(props: SlideProps) { + return ; +} + +export default function GlobalSnackbar() { + const { isOpen, message, severity, close, snackbarProps, alertProps } = useGlobalSnackbar(); + + const handleClose = (_event?: React.SyntheticEvent | Event, reason?: string) => { + if (reason === 'clickaway') return; + close(); + }; + + return ( + + + {message} + + + ); +} diff --git a/samples/SimpleFullStack/Web/src/ui/components/global_snackbar/useGlobalSnackbar.ts b/samples/SimpleFullStack/Web/src/ui/components/global_snackbar/useGlobalSnackbar.ts new file mode 100644 index 0000000..6330ccb --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/global_snackbar/useGlobalSnackbar.ts @@ -0,0 +1,41 @@ +import { create } from 'zustand'; +import type { SnackbarProps } from '@mui/material/Snackbar'; +import type { AlertColor, AlertProps } from '@mui/material/Alert'; + +export type GlobalSnackbarStore = { + isOpen: boolean; + message: string; + severity: AlertColor; + snackbarProps?: Omit< + SnackbarProps, + 'open' | 'onClose' | 'message' | 'children' + >; + alertProps?: Omit; + open: ( + message: string, + severity: AlertColor, + snackbarProps?: GlobalSnackbarStore['snackbarProps'], + alertProps?: GlobalSnackbarStore['alertProps'] + ) => void; + close: () => void; +}; + +const useGlobalSnackbar = create(set => ({ + isOpen: false, + message: '', + severity: 'success', + snackbarProps: undefined, + alertProps: undefined, + open: (message, severity, snackbarProps, alertProps) => + set({ isOpen: true, message, severity, snackbarProps, alertProps }), + close: () => + set({ + isOpen: false, + message: '', + severity: 'success', + snackbarProps: undefined, + alertProps: undefined, + }), +})); + +export default useGlobalSnackbar; diff --git a/samples/SimpleFullStack/Web/src/ui/components/layouts/MainLayout.tsx b/samples/SimpleFullStack/Web/src/ui/components/layouts/MainLayout.tsx new file mode 100644 index 0000000..272a08f --- /dev/null +++ b/samples/SimpleFullStack/Web/src/ui/components/layouts/MainLayout.tsx @@ -0,0 +1,28 @@ +import Box from '@mui/material/Box'; +import Footer from 'ui/components/footer/Footer'; +import { Outlet } from 'react-router-dom'; +import NavBar from '../nav/NavBar'; + + +const MainLayout = () => { + return ( + + + + + +