diff --git a/ModelCabinet.Server/Migrations/20250317212011_modifiedTagData.Designer.cs b/ModelCabinet.Server/Migrations/20250317212011_modifiedTagData.Designer.cs new file mode 100644 index 0000000..c26ade2 --- /dev/null +++ b/ModelCabinet.Server/Migrations/20250317212011_modifiedTagData.Designer.cs @@ -0,0 +1,667 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ModelCabinet.Server.Data; + +#nullable disable + +namespace ModelCabinet.Server.Migrations +{ + [DbContext(typeof(ModelCabinetContext))] + [Migration("20250317212011_modifiedTagData")] + partial class modifiedTagData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.13") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("AvatarUrl") + .HasColumnType("nvarchar(max)"); + + b.Property("Biography") + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("DateJoined") + .HasColumnType("datetime2"); + + b.Property("DisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("EmailNotificationsEnabled") + .HasColumnType("bit"); + + b.Property("GithubUsername") + .HasColumnType("nvarchar(max)"); + + b.Property("IsProfilePublic") + .HasColumnType("bit"); + + b.Property("LastActive") + .HasColumnType("datetime2"); + + b.Property("Location") + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NewMessageNotificationsEnabled") + .HasColumnType("bit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("PreferredLanguage") + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectUpdatesEnabled") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TimeZone") + .HasColumnType("nvarchar(max)"); + + b.Property("TwitterHandle") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Website") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Asset", b => + { + b.Property("AssetId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("AssetId")); + + b.Property("DateCreation") + .HasColumnType("datetime2"); + + b.Property("DateUpdated") + .HasColumnType("datetime2"); + + b.Property("FileSize") + .HasColumnType("bigint"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Path") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ProjectId") + .HasColumnType("int"); + + b.HasKey("AssetId"); + + b.HasIndex("ProjectId"); + + b.ToTable("Asset"); + + b.HasData( + new + { + AssetId = 1, + DateCreation = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2109), + DateUpdated = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2164), + FileSize = 446684L, + Name = "Test Asset", + Path = "Assets\\TestProject\\HelloWorld.stl", + ProjectId = 1 + }, + new + { + AssetId = 2, + DateCreation = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2169), + DateUpdated = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2171), + FileSize = 11285384L, + Name = "Benchy", + Path = "Assets\\TestProject\\3DBenchy.stl", + ProjectId = 1 + }); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.AssetTag", b => + { + b.Property("AssetId") + .HasColumnType("int"); + + b.Property("TagId") + .HasColumnType("int"); + + b.HasKey("AssetId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("AssetTag"); + + b.HasData( + new + { + AssetId = 2, + TagId = 1 + }, + new + { + AssetId = 2, + TagId = 5 + }, + new + { + AssetId = 2, + TagId = 2 + }, + new + { + AssetId = 1, + TagId = 4 + }, + new + { + AssetId = 1, + TagId = 6 + }); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Project", b => + { + b.Property("ProjectId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("ProjectId")); + + b.Property("Author") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CreationDate") + .HasColumnType("datetime2"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ModifiedDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ShortDescription") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Slug") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Version") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("ProjectId"); + + b.HasIndex("Slug") + .IsUnique(); + + b.ToTable("Project"); + + b.HasData( + new + { + ProjectId = 1, + Author = "Author", + CreationDate = new DateTime(2024, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Description", + ModifiedDate = new DateTime(2024, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Name = "Test Project", + ShortDescription = "Desc", + Slug = "nomen-est-omen", + Version = "0.0.1" + }, + new + { + ProjectId = 2, + Author = "Author", + CreationDate = new DateTime(2024, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Description = "Description", + ModifiedDate = new DateTime(2024, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), + Name = "Test Project Two", + ShortDescription = "Desc", + Slug = "nomen-est-bonum", + Version = "0.0.1" + }); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.ProjectTag", b => + { + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("TagId") + .HasColumnType("int"); + + b.HasKey("ProjectId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("ProjectTag"); + + b.HasData( + new + { + ProjectId = 1, + TagId = 3 + }, + new + { + ProjectId = 1, + TagId = 4 + }, + new + { + ProjectId = 1, + TagId = 5 + }, + new + { + ProjectId = 2, + TagId = 1 + }, + new + { + ProjectId = 2, + TagId = 2 + }, + new + { + ProjectId = 2, + TagId = 5 + }); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Tag", b => + { + b.Property("TagId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("TagId")); + + b.Property("Color") + .IsRequired() + .HasColumnType("char(6)"); + + b.Property("TagName") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("TagId"); + + b.HasIndex("TagName") + .IsUnique(); + + b.ToTable("Tag"); + + b.HasData( + new + { + TagId = 1, + Color = "fae033", + TagName = "Stress Test" + }, + new + { + TagId = 2, + Color = "df0000", + TagName = "D&D" + }, + new + { + TagId = 3, + Color = "40E0D0", + TagName = "Pathfinder" + }, + new + { + TagId = 4, + Color = "afafaf", + TagName = "Low Detail" + }, + new + { + TagId = 5, + Color = "3f3f3f", + TagName = "High Detail" + }, + new + { + TagId = 6, + Color = "23a300", + TagName = "Video Game" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("ModelCabinet.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("ModelCabinet.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ModelCabinet.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("ModelCabinet.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Asset", b => + { + b.HasOne("ModelCabinet.Server.Models.Project", null) + .WithMany("Assets") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.AssetTag", b => + { + b.HasOne("ModelCabinet.Server.Models.Asset", "Asset") + .WithMany("AssetTags") + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ModelCabinet.Server.Models.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Asset"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.ProjectTag", b => + { + b.HasOne("ModelCabinet.Server.Models.Project", "Project") + .WithMany("ProjectTags") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ModelCabinet.Server.Models.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Asset", b => + { + b.Navigation("AssetTags"); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Project", b => + { + b.Navigation("Assets"); + + b.Navigation("ProjectTags"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ModelCabinet.Server/Migrations/20250317212011_modifiedTagData.cs b/ModelCabinet.Server/Migrations/20250317212011_modifiedTagData.cs new file mode 100644 index 0000000..57a4434 --- /dev/null +++ b/ModelCabinet.Server/Migrations/20250317212011_modifiedTagData.cs @@ -0,0 +1,174 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace ModelCabinet.Server.Migrations +{ + /// + public partial class modifiedTagData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Tag", + columns: table => new + { + TagId = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + TagName = table.Column(type: "nvarchar(450)", nullable: false), + Color = table.Column(type: "char(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tag", x => x.TagId); + }); + + migrationBuilder.CreateTable( + name: "AssetTag", + columns: table => new + { + AssetId = table.Column(type: "int", nullable: false), + TagId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AssetTag", x => new { x.AssetId, x.TagId }); + table.ForeignKey( + name: "FK_AssetTag_Asset_AssetId", + column: x => x.AssetId, + principalTable: "Asset", + principalColumn: "AssetId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AssetTag_Tag_TagId", + column: x => x.TagId, + principalTable: "Tag", + principalColumn: "TagId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ProjectTag", + columns: table => new + { + ProjectId = table.Column(type: "int", nullable: false), + TagId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ProjectTag", x => new { x.ProjectId, x.TagId }); + table.ForeignKey( + name: "FK_ProjectTag_Project_ProjectId", + column: x => x.ProjectId, + principalTable: "Project", + principalColumn: "ProjectId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ProjectTag_Tag_TagId", + column: x => x.TagId, + principalTable: "Tag", + principalColumn: "TagId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.UpdateData( + table: "Asset", + keyColumn: "AssetId", + keyValue: 1, + columns: new[] { "DateCreation", "DateUpdated" }, + values: new object[] { new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2109), new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2164) }); + + migrationBuilder.UpdateData( + table: "Asset", + keyColumn: "AssetId", + keyValue: 2, + columns: new[] { "DateCreation", "DateUpdated" }, + values: new object[] { new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2169), new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2171) }); + + migrationBuilder.InsertData( + table: "Tag", + columns: new[] { "TagId", "Color", "TagName" }, + values: new object[,] + { + { 1, "fae033", "Stress Test" }, + { 2, "df0000", "D&D" }, + { 3, "40E0D0", "Pathfinder" }, + { 4, "afafaf", "Low Detail" }, + { 5, "3f3f3f", "High Detail" }, + { 6, "23a300", "Video Game" } + }); + + migrationBuilder.InsertData( + table: "AssetTag", + columns: new[] { "AssetId", "TagId" }, + values: new object[,] + { + { 1, 4 }, + { 1, 6 }, + { 2, 1 }, + { 2, 2 }, + { 2, 5 } + }); + + migrationBuilder.InsertData( + table: "ProjectTag", + columns: new[] { "ProjectId", "TagId" }, + values: new object[,] + { + { 1, 3 }, + { 1, 4 }, + { 1, 5 }, + { 2, 1 }, + { 2, 2 }, + { 2, 5 } + }); + + migrationBuilder.CreateIndex( + name: "IX_AssetTag_TagId", + table: "AssetTag", + column: "TagId"); + + migrationBuilder.CreateIndex( + name: "IX_ProjectTag_TagId", + table: "ProjectTag", + column: "TagId"); + + migrationBuilder.CreateIndex( + name: "IX_Tag_TagName", + table: "Tag", + column: "TagName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AssetTag"); + + migrationBuilder.DropTable( + name: "ProjectTag"); + + migrationBuilder.DropTable( + name: "Tag"); + + migrationBuilder.UpdateData( + table: "Asset", + keyColumn: "AssetId", + keyValue: 1, + columns: new[] { "DateCreation", "DateUpdated" }, + values: new object[] { new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5697), new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5756) }); + + migrationBuilder.UpdateData( + table: "Asset", + keyColumn: "AssetId", + keyValue: 2, + columns: new[] { "DateCreation", "DateUpdated" }, + values: new object[] { new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5760), new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5761) }); + } + } +} diff --git a/ModelCabinet.Server/Migrations/ModelCabinetContextModelSnapshot.cs b/ModelCabinet.Server/Migrations/ModelCabinetContextModelSnapshot.cs index e63828b..689cd9b 100644 --- a/ModelCabinet.Server/Migrations/ModelCabinetContextModelSnapshot.cs +++ b/ModelCabinet.Server/Migrations/ModelCabinetContextModelSnapshot.cs @@ -301,14 +301,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ProjectId"); - b.ToTable("Asset", (string)null); + b.ToTable("Asset"); b.HasData( new { AssetId = 1, - DateCreation = new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5697), - DateUpdated = new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5756), + DateCreation = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2109), + DateUpdated = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2164), FileSize = 446684L, Name = "Test Asset", Path = "Assets\\TestProject\\HelloWorld.stl", @@ -317,8 +317,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { AssetId = 2, - DateCreation = new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5760), - DateUpdated = new DateTime(2025, 2, 25, 14, 39, 3, 727, DateTimeKind.Local).AddTicks(5761), + DateCreation = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2169), + DateUpdated = new DateTime(2025, 3, 17, 14, 20, 10, 930, DateTimeKind.Local).AddTicks(2171), FileSize = 11285384L, Name = "Benchy", Path = "Assets\\TestProject\\3DBenchy.stl", @@ -326,6 +326,48 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity("ModelCabinet.Server.Models.AssetTag", b => + { + b.Property("AssetId") + .HasColumnType("int"); + + b.Property("TagId") + .HasColumnType("int"); + + b.HasKey("AssetId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("AssetTag"); + + b.HasData( + new + { + AssetId = 2, + TagId = 1 + }, + new + { + AssetId = 2, + TagId = 5 + }, + new + { + AssetId = 2, + TagId = 2 + }, + new + { + AssetId = 1, + TagId = 4 + }, + new + { + AssetId = 1, + TagId = 6 + }); + }); + modelBuilder.Entity("ModelCabinet.Server.Models.Project", b => { b.Property("ProjectId") @@ -369,7 +411,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Slug") .IsUnique(); - b.ToTable("Project", (string)null); + b.ToTable("Project"); b.HasData( new @@ -398,6 +440,115 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); }); + modelBuilder.Entity("ModelCabinet.Server.Models.ProjectTag", b => + { + b.Property("ProjectId") + .HasColumnType("int"); + + b.Property("TagId") + .HasColumnType("int"); + + b.HasKey("ProjectId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("ProjectTag"); + + b.HasData( + new + { + ProjectId = 1, + TagId = 3 + }, + new + { + ProjectId = 1, + TagId = 4 + }, + new + { + ProjectId = 1, + TagId = 5 + }, + new + { + ProjectId = 2, + TagId = 1 + }, + new + { + ProjectId = 2, + TagId = 2 + }, + new + { + ProjectId = 2, + TagId = 5 + }); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Tag", b => + { + b.Property("TagId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("TagId")); + + b.Property("Color") + .IsRequired() + .HasColumnType("char(6)"); + + b.Property("TagName") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("TagId"); + + b.HasIndex("TagName") + .IsUnique(); + + b.ToTable("Tag"); + + b.HasData( + new + { + TagId = 1, + Color = "fae033", + TagName = "Stress Test" + }, + new + { + TagId = 2, + Color = "df0000", + TagName = "D&D" + }, + new + { + TagId = 3, + Color = "40E0D0", + TagName = "Pathfinder" + }, + new + { + TagId = 4, + Color = "afafaf", + TagName = "Low Detail" + }, + new + { + TagId = 5, + Color = "3f3f3f", + TagName = "High Detail" + }, + new + { + TagId = 6, + Color = "23a300", + TagName = "Video Game" + }); + }); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) @@ -458,9 +609,54 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("ModelCabinet.Server.Models.AssetTag", b => + { + b.HasOne("ModelCabinet.Server.Models.Asset", "Asset") + .WithMany("AssetTags") + .HasForeignKey("AssetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ModelCabinet.Server.Models.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Asset"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.ProjectTag", b => + { + b.HasOne("ModelCabinet.Server.Models.Project", "Project") + .WithMany("ProjectTags") + .HasForeignKey("ProjectId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ModelCabinet.Server.Models.Tag", "Tag") + .WithMany() + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("ModelCabinet.Server.Models.Asset", b => + { + b.Navigation("AssetTags"); + }); + modelBuilder.Entity("ModelCabinet.Server.Models.Project", b => { b.Navigation("Assets"); + + b.Navigation("ProjectTags"); }); #pragma warning restore 612, 618 } diff --git a/modelcabinet.client/src/app/Models/asset.ts b/modelcabinet.client/src/app/Models/asset.ts index a7f4b5f..10e0fa8 100644 --- a/modelcabinet.client/src/app/Models/asset.ts +++ b/modelcabinet.client/src/app/Models/asset.ts @@ -9,6 +9,9 @@ export interface Asset { fileSize: number, projectId: number, assetTags: AssetTag[] + + // Read-only property + readonly assetTagNames: string[] } // Used to Match the structure in the backend @@ -27,5 +30,6 @@ export const emptyAsset: Asset = { dateUpdated: new Date("2025-03-10"), fileSize: 0, projectId: 0, - assetTags: [] -} \ No newline at end of file + assetTags: [], + get assetTagNames() { return []; } +} diff --git a/modelcabinet.client/src/app/asset-utils.ts b/modelcabinet.client/src/app/asset-utils.ts new file mode 100644 index 0000000..1ff87de --- /dev/null +++ b/modelcabinet.client/src/app/asset-utils.ts @@ -0,0 +1,41 @@ +import { Asset } from './Models/asset'; + +/** + * Returns an array of tag names from an asset's assetTags array + * @param asset The asset to extract tag names from + * @returns string[] Array of tag names + */ +export function getAssetTagNames(asset: Asset): string[] { + if (!asset.assetTags || asset.assetTags.length === 0) { + return []; + } + + return asset.assetTags.map(assetTag => assetTag.tag.tagName); +} + +/** + * Creates a new Asset object with assetTagNames getter implemented + * @param asset Original asset object from API + * @returns Asset with assetTagNames getter implemented + */ +export function createAssetWithTagNames(asset: Asset): Asset { + // Create a new object with everything from the original asset + const processedAsset = { + ...asset, + // Define getter for assetTagNames + get assetTagNames(): string[] { + return getAssetTagNames(this); + } + }; + + return processedAsset; +} + +/** + * Process multiple assets to add assetTagNames getters + * @param assets Array of assets from API + * @returns Array of assets with assetTagNames getters + */ +export function processAssetsWithTagNames(assets: Asset[]): Asset[] { + return assets.map(asset => createAssetWithTagNames(asset)); +} diff --git a/modelcabinet.client/src/app/data.service.ts b/modelcabinet.client/src/app/data.service.ts index f7d1a73..49e44c3 100644 --- a/modelcabinet.client/src/app/data.service.ts +++ b/modelcabinet.client/src/app/data.service.ts @@ -4,6 +4,7 @@ import { BehaviorSubject, Observable, tap } from "rxjs"; import { emptyProject, Project } from "./Models/project"; import { Asset, emptyAsset } from "./Models/asset"; import { emptyTag, Tag } from "./Models/tag"; +import { createAssetWithTagNames, processAssetsWithTagNames } from "./asset-utils"; //import { Tag } from "./Models/tag"; @@ -57,9 +58,19 @@ export class DataService { } getProjectById(id: number) { - this.http.get(`/api/Projects/${id}`).subscribe(data => { - this.project$.next(data); - // this.assets$.next(data.asset.&values); + this.http.get(`/api/Projects/${id}`).subscribe({ + next: (data) => { + // Process assets in the project + if (data.assets && data.assets.length > 0) { + data.assets = processAssetsWithTagNames(data.assets); + } + console.log("Project loaded successfully:", data); + this.project$.next(data); + }, + error: (err) => { + console.error("Error loading project:", err); + // Don't update the BehaviorSubject with invalid data + } }); } @@ -93,31 +104,40 @@ export class DataService { createAsset(asset: Asset) { this.http.post(`/api/Assets`, asset).subscribe(data => { - this.asset$.next(data); + const processedAsset = createAssetWithTagNames(data); + this.asset$.next(processedAsset); }); } getAllAssets() { this.http.get(`/api/Assets`).subscribe(data => { - this.assets$.next(data); + // Process assets to add the assetTagNames property + const processedAssets = processAssetsWithTagNames(data); + this.assets$.next(processedAssets); }); } getAssetsById(id: number) { this.http.get(`/api/Assets/${id}`).subscribe(data => { - this.asset$.next(data); - }) + // Add the assetTagNames property + const processedAsset = createAssetWithTagNames(data); + this.asset$.next(processedAsset); + }); } - updateAssetById(id:number, asset: Asset) { + updateAssetById(id: number, asset: Asset) { this.http.put(`/api/Assets/${id}`, asset).subscribe(data => { - this.asset$.next(data); + const processedAsset = createAssetWithTagNames(data); + this.asset$.next(processedAsset); }); } deleteAssetById(id: number) { this.http.delete(`/api/Assets/${id}`).subscribe(data => { - this.asset$.next(data); + if (data) { + const processedAsset = createAssetWithTagNames(data); + this.asset$.next(processedAsset); + } }); } @@ -157,4 +177,17 @@ export class DataService { return data; }); } + + getAllUniqueTags(): string[] { + const assets = this.assets$.value; + const allTags = new Set(); + + assets.forEach(asset => { + if (asset.assetTagNames) { + asset.assetTagNames.forEach(tag => allTags.add(tag)); + } + }); + + return Array.from(allTags).sort(); + } } diff --git a/modelcabinet.client/src/app/projects/project-page/project-page.component.html b/modelcabinet.client/src/app/projects/project-page/project-page.component.html index 1fbab51..4d50dc9 100644 --- a/modelcabinet.client/src/app/projects/project-page/project-page.component.html +++ b/modelcabinet.client/src/app/projects/project-page/project-page.component.html @@ -1,37 +1,46 @@ -@let project = project$ | async; + +
+
+
+ Loading... +
+
+

Loading project data...

+
-
+ +
- +
-

{{ project!.name }}

+

{{ project$.value.name }}

Description:

-

{{ project!.shortDescription }}

+

{{ project$.value.shortDescription }}

-

Author:
{{ project!.author }}

+

Author:
{{ project$.value.author }}

-

Creation Date:
{{ project!.creationDate | date:'short' }}

+

Creation Date:
{{ project$.value.creationDate | date:'short' }}

-

Modified Date:
{{ project!.modifiedDate | date:'short' }}

+

Modified Date:
{{ project$.value.modifiedDate | date:'short' }}

-
+
    -
  • +
  • {{ asset.name }}
  • @@ -39,54 +48,63 @@

    {{ project!.name }}

-
+
Tags:
- + +
+
+
+ + +
+
+
+
Filter Assets by Tag
+
+
+ +
+
+ +
+
-
Assets Included In Project:
-
+
Assets Included In Project:
+
-
+
+ +
+ No assets match the selected tag filter. +
+
+ + +
+
+ This project has no assets. +
- -