diff --git a/src/Analysim.Core/Entities/ProjectComment.cs b/src/Analysim.Core/Entities/ProjectComment.cs index c2af1f30..cce032a7 100644 --- a/src/Analysim.Core/Entities/ProjectComment.cs +++ b/src/Analysim.Core/Entities/ProjectComment.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; diff --git a/src/Analysim.Core/Entities/ProjectLog.cs b/src/Analysim.Core/Entities/ProjectLog.cs index 8b8996f2..a2f8a481 100644 --- a/src/Analysim.Core/Entities/ProjectLog.cs +++ b/src/Analysim.Core/Entities/ProjectLog.cs @@ -1,3 +1,5 @@ +#nullable enable // Enable nullable fields + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -21,6 +23,13 @@ public class ProjectLog public int ProjectID { get; set; } public Project Project{ get; set; } = null!; + // Referenced Notebook + [ForeignKey("ReferencedNotebookID")] + public int? ReferencedNotebookID { get; set; } + public Notebook? ReferencedNotebook { get; set; } + + public int? ReferencedNotebookVersion { get; set; } + // Project Log Title public string? Title { get; set; } diff --git a/src/Analysim.Core/Entities/Publication.cs b/src/Analysim.Core/Entities/Publication.cs index 4887039c..518d0260 100644 --- a/src/Analysim.Core/Entities/Publication.cs +++ b/src/Analysim.Core/Entities/Publication.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -30,6 +32,11 @@ public class Publication [Required(ErrorMessage = "Publication Year is a required field.")] [Range(1, 2100, ErrorMessage = "Enter a valid publication year.")] public int? Year { get; set; } + + public string? Volume { get; set; } + public string? Issue { get; set; } + public string? Pages { get; set; } + public string? Notes { get; set; } // Timestamps diff --git a/src/Analysim.Infrastructure/Data/ApplicationDbContext.cs b/src/Analysim.Infrastructure/Data/ApplicationDbContext.cs index cdae1610..0e8812a1 100644 --- a/src/Analysim.Infrastructure/Data/ApplicationDbContext.cs +++ b/src/Analysim.Infrastructure/Data/ApplicationDbContext.cs @@ -206,6 +206,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(pl => pl.BlobFileID) .OnDelete(DeleteBehavior.SetNull); + // Optional One To Many Relationship (Notebook -> ProjectLog) + modelBuilder.Entity() + .HasOne(pl => pl.ReferencedNotebook) + .WithMany() + .HasForeignKey(pl => pl.ReferencedNotebookID) + .OnDelete(DeleteBehavior.SetNull); + // One To Many Relationship (ProjectLog -> ProjectComment) modelBuilder.Entity() .HasMany(pl => pl.Comments) @@ -222,6 +229,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .HasIndex(pc => pc.ProjectLogID); + modelBuilder.Entity() + .HasIndex(pl => pl.ReferencedNotebookID); + #endregion // One To Many Relationship (Project -> Publication) diff --git a/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index d23e92b5..7989d6d6 100644 --- a/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -251,6 +251,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("CommentID")); b.Property("Content") + .IsRequired() .HasColumnType("text"); b.Property("CreatedAt") @@ -365,6 +366,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ProjectID") .HasColumnType("integer"); + b.Property("ReferencedNotebookID") + .HasColumnType("integer"); + + b.Property("ReferencedNotebookVersion") + .HasColumnType("integer"); + b.Property("Title") .HasColumnType("text"); @@ -380,6 +387,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("ProjectID"); + b.HasIndex("ReferencedNotebookID"); + b.HasIndex("UserID"); b.ToTable("ProjectLogs"); @@ -440,6 +449,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Doi") .HasColumnType("text"); + b.Property("Issue") + .HasColumnType("text"); + b.Property("Journal") .IsRequired() .HasColumnType("text"); @@ -447,6 +459,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Notes") .HasColumnType("text"); + b.Property("Pages") + .HasColumnType("text"); + b.Property("ProjectID") .HasColumnType("integer"); @@ -460,6 +475,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Url") .HasColumnType("text"); + b.Property("Volume") + .HasColumnType("text"); + b.Property("Year") .IsRequired() .HasColumnType("integer"); @@ -619,21 +637,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = 1, - ConcurrencyStamp = "e7431394-d3f8-4e7a-828d-930d0ecfe1ac", + ConcurrencyStamp = "d7c6278e-3f78-4db6-8832-aa41ea9fd62c", Name = "Admin", NormalizedName = "ADMIN" }, new { Id = 2, - ConcurrencyStamp = "b325405e-6f19-4b1c-a845-9df31e386760", + ConcurrencyStamp = "7383e31e-a884-4cd6-82f7-a6287b4e30a5", Name = "Customer", NormalizedName = "CUSTOMER" }, new { Id = 3, - ConcurrencyStamp = "f68ee1fe-5d74-4f56-ade1-5a8837a3e127", + ConcurrencyStamp = "398ef1f2-6b82-45f2-9601-ed2129f197e9", Name = "Moderator", NormalizedName = "MODERATOR" }); @@ -887,6 +905,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("Core.Entities.Notebook", "ReferencedNotebook") + .WithMany() + .HasForeignKey("ReferencedNotebookID") + .OnDelete(DeleteBehavior.SetNull); + b.HasOne("Core.Entities.User", "User") .WithMany("ProjectLogs") .HasForeignKey("UserID") @@ -897,6 +920,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Project"); + b.Navigation("ReferencedNotebook"); + b.Navigation("User"); }); diff --git a/src/Analysim.Infrastructure/Migrations/20260519132728_AddNotebookRefToProjectLogs.Designer.cs b/src/Analysim.Infrastructure/Migrations/20260519132728_AddNotebookRefToProjectLogs.Designer.cs new file mode 100644 index 00000000..77d83dc4 --- /dev/null +++ b/src/Analysim.Infrastructure/Migrations/20260519132728_AddNotebookRefToProjectLogs.Designer.cs @@ -0,0 +1,1105 @@ +// +using System; +using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260519132728_AddNotebookRefToProjectLogs")] + partial class AddNotebookRefToProjectLogs + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Analysim.Core.Entities.BlobFileContent", b => + { + b.Property("BlobFileID") + .HasColumnType("integer"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("BlobFileID"); + + b.ToTable("BlobFileContent"); + }); + + modelBuilder.Entity("Analysim.Core.Entities.NotebookContent", b => + { + b.Property("NotebookID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.Property("Version") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Size") + .HasColumnType("integer"); + + b.HasKey("NotebookID", "Version"); + + b.ToTable("NotebookContent"); + }); + + modelBuilder.Entity("Core.Entities.BlobFile", b => + { + b.Property("BlobFileID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BlobFileID")); + + b.Property("Container") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Directory") + .IsRequired() + .HasColumnType("text"); + + b.Property("Extension") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("Size") + .HasColumnType("integer"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("BlobFileID"); + + b.HasIndex("ProjectID"); + + b.HasIndex("UserID"); + + b.ToTable("BlobFiles"); + }); + + modelBuilder.Entity("Core.Entities.Notebook", b => + { + b.Property("NotebookID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("NotebookID")); + + b.Property("Container") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Directory") + .IsRequired() + .HasColumnType("text"); + + b.Property("Extension") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("Route") + .IsRequired() + .HasColumnType("text"); + + b.Property("Size") + .HasColumnType("integer"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("text"); + + b.Property("type") + .HasColumnType("text"); + + b.HasKey("NotebookID"); + + b.HasIndex("Directory"); + + b.HasIndex("ProjectID"); + + b.ToTable("Notebook"); + }); + + modelBuilder.Entity("Core.Entities.ObservableNotebookDataset", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ID")); + + b.Property("BlobFileID") + .HasColumnType("integer"); + + b.Property("NotebookID") + .HasColumnType("integer"); + + b.Property("datasetName") + .HasColumnType("text"); + + b.Property("datasetURL") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("NotebookID"); + + b.ToTable("ObservableNotebookDataset"); + }); + + modelBuilder.Entity("Core.Entities.Project", b => + { + b.Property("ProjectID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ProjectID")); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ForkedFromProjectID") + .HasColumnType("integer"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Route") + .IsRequired() + .HasColumnType("text"); + + b.Property("Visibility") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ProjectID"); + + b.ToTable("Projects"); + }); + + modelBuilder.Entity("Core.Entities.ProjectComment", b => + { + b.Property("CommentID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("CommentID")); + + b.Property("Content") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsPendingReview") + .HasColumnType("boolean"); + + b.Property("ParentCommentID") + .HasColumnType("integer"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("ProjectLogID") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("CommentID"); + + b.HasIndex("ParentCommentID"); + + b.HasIndex("ProjectID"); + + b.HasIndex("ProjectLogID"); + + b.HasIndex("UserID"); + + b.ToTable("ProjectComments"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentFlag", b => + { + b.Property("FlagID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("FlagID")); + + b.Property("CommentContentSnapshot") + .HasColumnType("text"); + + b.Property("CommentID") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("FlagID"); + + b.HasIndex("UserID"); + + b.HasIndex("CommentID", "UserID") + .IsUnique(); + + b.ToTable("ProjectCommentFlags"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentLike", b => + { + b.Property("CommentID") + .HasColumnType("integer"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CommentID", "UserID"); + + b.HasIndex("UserID"); + + b.ToTable("ProjectCommentLikes"); + }); + + modelBuilder.Entity("Core.Entities.ProjectLog", b => + { + b.Property("LogID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LogID")); + + b.Property("BlobFileID") + .HasColumnType("integer"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("ReferencedNotebookID") + .HasColumnType("integer"); + + b.Property("ReferencedNotebookVersion") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("LogID"); + + b.HasIndex("BlobFileID"); + + b.HasIndex("ProjectID"); + + b.HasIndex("ReferencedNotebookID"); + + b.HasIndex("UserID"); + + b.ToTable("ProjectLogs"); + }); + + modelBuilder.Entity("Core.Entities.ProjectTag", b => + { + b.Property("ProjectID") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.Property("TagID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.HasKey("ProjectID", "TagID"); + + b.HasIndex("TagID"); + + b.ToTable("ProjectTags"); + }); + + modelBuilder.Entity("Core.Entities.ProjectUser", b => + { + b.Property("UserID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.Property("ProjectID") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.Property("IsFollowing") + .HasColumnType("boolean"); + + b.Property("UserRole") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("UserID", "ProjectID"); + + b.HasIndex("ProjectID"); + + b.ToTable("ProjectUsers"); + }); + + modelBuilder.Entity("Core.Entities.Publication", b => + { + b.Property("PublicationID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PublicationID")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Doi") + .HasColumnType("text"); + + b.Property("Journal") + .IsRequired() + .HasColumnType("text"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("SourceAuthor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("Year") + .IsRequired() + .HasColumnType("integer"); + + b.HasKey("PublicationID"); + + b.HasIndex("ProjectID"); + + b.ToTable("Publications"); + }); + + modelBuilder.Entity("Core.Entities.Tag", b => + { + b.Property("TagID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("TagID")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("TagID"); + + b.ToTable("Tag"); + }); + + modelBuilder.Entity("Core.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("Bio") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LastOnline") + .HasColumnType("timestamp with time zone"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("RegistrationSurvey") + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Core.Entities.UserUser", b => + { + b.Property("UserID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.Property("FollowerID") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.HasKey("UserID", "FollowerID"); + + b.HasIndex("FollowerID"); + + b.ToTable("UserUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = 1, + ConcurrencyStamp = "f8accda8-f6e5-43e2-acdc-ec7489690c7e", + Name = "Admin", + NormalizedName = "ADMIN" + }, + new + { + Id = 2, + ConcurrencyStamp = "5acac352-55a0-4b09-bc33-6a734391391b", + Name = "Customer", + NormalizedName = "CUSTOMER" + }, + new + { + Id = 3, + ConcurrencyStamp = "67d7e55c-aee8-4fe1-8cb4-c89f670c3c24", + Name = "Moderator", + NormalizedName = "MODERATOR" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("integer"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Analysim.Core.Entities.BlobFileContent", b => + { + b.HasOne("Core.Entities.BlobFile", "BlobFile") + .WithMany("BlobFileContents") + .HasForeignKey("BlobFileID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlobFile"); + }); + + modelBuilder.Entity("Analysim.Core.Entities.NotebookContent", b => + { + b.HasOne("Core.Entities.Notebook", "Notebook") + .WithMany("NotebookContents") + .HasForeignKey("NotebookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notebook"); + }); + + modelBuilder.Entity("Core.Entities.BlobFile", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("BlobFiles") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Core.Entities.User", "User") + .WithMany("BlobFiles") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.Notebook", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("Notebooks") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Core.Entities.ObservableNotebookDataset", b => + { + b.HasOne("Core.Entities.Notebook", "notebook") + .WithMany("observableNotebookDatasets") + .HasForeignKey("NotebookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("notebook"); + }); + + modelBuilder.Entity("Core.Entities.ProjectComment", b => + { + b.HasOne("Core.Entities.ProjectComment", "ParentComment") + .WithMany("Replies") + .HasForeignKey("ParentCommentID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectComments") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.ProjectLog", "ProjectLog") + .WithMany("Comments") + .HasForeignKey("ProjectLogID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Core.Entities.User", "User") + .WithMany("ProjectComments") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ParentComment"); + + b.Navigation("Project"); + + b.Navigation("ProjectLog"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentFlag", b => + { + b.HasOne("Core.Entities.ProjectComment", "ProjectComment") + .WithMany("CommentFlags") + .HasForeignKey("CommentID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("CommentFlags") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProjectComment"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentLike", b => + { + b.HasOne("Core.Entities.ProjectComment", "ProjectComment") + .WithMany("CommentLikes") + .HasForeignKey("CommentID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("CommentLikes") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProjectComment"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectLog", b => + { + b.HasOne("Core.Entities.BlobFile", "BlobFile") + .WithMany() + .HasForeignKey("BlobFileID") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectLogs") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.Notebook", "ReferencedNotebook") + .WithMany() + .HasForeignKey("ReferencedNotebookID") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Core.Entities.User", "User") + .WithMany("ProjectLogs") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BlobFile"); + + b.Navigation("Project"); + + b.Navigation("ReferencedNotebook"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectTag", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectTags") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.Tag", "Tag") + .WithMany("ProjectTags") + .HasForeignKey("TagID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("Core.Entities.ProjectUser", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectUsers") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("ProjectUsers") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.Publication", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("Publications") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Core.Entities.UserUser", b => + { + b.HasOne("Core.Entities.User", "Follower") + .WithMany("Following") + .HasForeignKey("FollowerID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("Followers") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Follower"); + + b.Navigation("User"); + }); + + 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("Core.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Core.Entities.User", 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("Core.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Core.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Core.Entities.BlobFile", b => + { + b.Navigation("BlobFileContents"); + }); + + modelBuilder.Entity("Core.Entities.Notebook", b => + { + b.Navigation("NotebookContents"); + + b.Navigation("observableNotebookDatasets"); + }); + + modelBuilder.Entity("Core.Entities.Project", b => + { + b.Navigation("BlobFiles"); + + b.Navigation("Notebooks"); + + b.Navigation("ProjectComments"); + + b.Navigation("ProjectLogs"); + + b.Navigation("ProjectTags"); + + b.Navigation("ProjectUsers"); + + b.Navigation("Publications"); + }); + + modelBuilder.Entity("Core.Entities.ProjectComment", b => + { + b.Navigation("CommentFlags"); + + b.Navigation("CommentLikes"); + + b.Navigation("Replies"); + }); + + modelBuilder.Entity("Core.Entities.ProjectLog", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Core.Entities.Tag", b => + { + b.Navigation("ProjectTags"); + }); + + modelBuilder.Entity("Core.Entities.User", b => + { + b.Navigation("BlobFiles"); + + b.Navigation("CommentFlags"); + + b.Navigation("CommentLikes"); + + b.Navigation("Followers"); + + b.Navigation("Following"); + + b.Navigation("ProjectComments"); + + b.Navigation("ProjectLogs"); + + b.Navigation("ProjectUsers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Analysim.Infrastructure/Migrations/20260519132728_AddNotebookRefToProjectLogs.cs b/src/Analysim.Infrastructure/Migrations/20260519132728_AddNotebookRefToProjectLogs.cs new file mode 100644 index 00000000..45f6040e --- /dev/null +++ b/src/Analysim.Infrastructure/Migrations/20260519132728_AddNotebookRefToProjectLogs.cs @@ -0,0 +1,98 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + public partial class AddNotebookRefToProjectLogs : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ReferencedNotebookID", + table: "ProjectLogs", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "ReferencedNotebookVersion", + table: "ProjectLogs", + type: "integer", + nullable: true); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 1, + column: "ConcurrencyStamp", + value: "f8accda8-f6e5-43e2-acdc-ec7489690c7e"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 2, + column: "ConcurrencyStamp", + value: "5acac352-55a0-4b09-bc33-6a734391391b"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 3, + column: "ConcurrencyStamp", + value: "67d7e55c-aee8-4fe1-8cb4-c89f670c3c24"); + + migrationBuilder.CreateIndex( + name: "IX_ProjectLogs_ReferencedNotebookID", + table: "ProjectLogs", + column: "ReferencedNotebookID"); + + migrationBuilder.AddForeignKey( + name: "FK_ProjectLogs_Notebook_ReferencedNotebookID", + table: "ProjectLogs", + column: "ReferencedNotebookID", + principalTable: "Notebook", + principalColumn: "NotebookID", + onDelete: ReferentialAction.SetNull); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ProjectLogs_Notebook_ReferencedNotebookID", + table: "ProjectLogs"); + + migrationBuilder.DropIndex( + name: "IX_ProjectLogs_ReferencedNotebookID", + table: "ProjectLogs"); + + migrationBuilder.DropColumn( + name: "ReferencedNotebookID", + table: "ProjectLogs"); + + migrationBuilder.DropColumn( + name: "ReferencedNotebookVersion", + table: "ProjectLogs"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 1, + column: "ConcurrencyStamp", + value: "f1d80f82-74ad-4f0e-b39a-228c651b424a"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 2, + column: "ConcurrencyStamp", + value: "cfb8a477-eb7b-4e91-865d-122d17565ce0"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 3, + column: "ConcurrencyStamp", + value: "ecaa6f71-1595-44f7-9337-57b461da675a"); + } + } +} diff --git a/src/Analysim.Infrastructure/Migrations/20260520182711_AddVolIssuePagesToPublications.Designer.cs b/src/Analysim.Infrastructure/Migrations/20260520182711_AddVolIssuePagesToPublications.Designer.cs new file mode 100644 index 00000000..34e31421 --- /dev/null +++ b/src/Analysim.Infrastructure/Migrations/20260520182711_AddVolIssuePagesToPublications.Designer.cs @@ -0,0 +1,1118 @@ +// +using System; +using Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260520182711_AddVolIssuePagesToPublications")] + partial class AddVolIssuePagesToPublications + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Analysim.Core.Entities.BlobFileContent", b => + { + b.Property("BlobFileID") + .HasColumnType("integer"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.HasKey("BlobFileID"); + + b.ToTable("BlobFileContent"); + }); + + modelBuilder.Entity("Analysim.Core.Entities.NotebookContent", b => + { + b.Property("NotebookID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.Property("Version") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("Content") + .IsRequired() + .HasColumnType("bytea"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Size") + .HasColumnType("integer"); + + b.HasKey("NotebookID", "Version"); + + b.ToTable("NotebookContent"); + }); + + modelBuilder.Entity("Core.Entities.BlobFile", b => + { + b.Property("BlobFileID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("BlobFileID")); + + b.Property("Container") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Directory") + .IsRequired() + .HasColumnType("text"); + + b.Property("Extension") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("Size") + .HasColumnType("integer"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("BlobFileID"); + + b.HasIndex("ProjectID"); + + b.HasIndex("UserID"); + + b.ToTable("BlobFiles"); + }); + + modelBuilder.Entity("Core.Entities.Notebook", b => + { + b.Property("NotebookID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("NotebookID")); + + b.Property("Container") + .IsRequired() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Directory") + .IsRequired() + .HasColumnType("text"); + + b.Property("Extension") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("Route") + .IsRequired() + .HasColumnType("text"); + + b.Property("Size") + .HasColumnType("integer"); + + b.Property("Uri") + .IsRequired() + .HasColumnType("text"); + + b.Property("type") + .HasColumnType("text"); + + b.HasKey("NotebookID"); + + b.HasIndex("Directory"); + + b.HasIndex("ProjectID"); + + b.ToTable("Notebook"); + }); + + modelBuilder.Entity("Core.Entities.ObservableNotebookDataset", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ID")); + + b.Property("BlobFileID") + .HasColumnType("integer"); + + b.Property("NotebookID") + .HasColumnType("integer"); + + b.Property("datasetName") + .HasColumnType("text"); + + b.Property("datasetURL") + .HasColumnType("text"); + + b.HasKey("ID"); + + b.HasIndex("NotebookID"); + + b.ToTable("ObservableNotebookDataset"); + }); + + modelBuilder.Entity("Core.Entities.Project", b => + { + b.Property("ProjectID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("ProjectID")); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .HasMaxLength(500) + .HasColumnType("character varying(500)"); + + b.Property("ForkedFromProjectID") + .HasColumnType("integer"); + + b.Property("LastUpdated") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.Property("Route") + .IsRequired() + .HasColumnType("text"); + + b.Property("Visibility") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("ProjectID"); + + b.ToTable("Projects"); + }); + + modelBuilder.Entity("Core.Entities.ProjectComment", b => + { + b.Property("CommentID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("CommentID")); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("IsPendingReview") + .HasColumnType("boolean"); + + b.Property("ParentCommentID") + .HasColumnType("integer"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("ProjectLogID") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("CommentID"); + + b.HasIndex("ParentCommentID"); + + b.HasIndex("ProjectID"); + + b.HasIndex("ProjectLogID"); + + b.HasIndex("UserID"); + + b.ToTable("ProjectComments"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentFlag", b => + { + b.Property("FlagID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("FlagID")); + + b.Property("CommentContentSnapshot") + .HasColumnType("text"); + + b.Property("CommentID") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("FlagID"); + + b.HasIndex("UserID"); + + b.HasIndex("CommentID", "UserID") + .IsUnique(); + + b.ToTable("ProjectCommentFlags"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentLike", b => + { + b.Property("CommentID") + .HasColumnType("integer"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("CommentID", "UserID"); + + b.HasIndex("UserID"); + + b.ToTable("ProjectCommentLikes"); + }); + + modelBuilder.Entity("Core.Entities.ProjectLog", b => + { + b.Property("LogID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("LogID")); + + b.Property("BlobFileID") + .HasColumnType("integer"); + + b.Property("Content") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("boolean"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("ReferencedNotebookID") + .HasColumnType("integer"); + + b.Property("ReferencedNotebookVersion") + .HasColumnType("integer"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UserID") + .HasColumnType("integer"); + + b.HasKey("LogID"); + + b.HasIndex("BlobFileID"); + + b.HasIndex("ProjectID"); + + b.HasIndex("ReferencedNotebookID"); + + b.HasIndex("UserID"); + + b.ToTable("ProjectLogs"); + }); + + modelBuilder.Entity("Core.Entities.ProjectTag", b => + { + b.Property("ProjectID") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.Property("TagID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.HasKey("ProjectID", "TagID"); + + b.HasIndex("TagID"); + + b.ToTable("ProjectTags"); + }); + + modelBuilder.Entity("Core.Entities.ProjectUser", b => + { + b.Property("UserID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.Property("ProjectID") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.Property("IsFollowing") + .HasColumnType("boolean"); + + b.Property("UserRole") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("UserID", "ProjectID"); + + b.HasIndex("ProjectID"); + + b.ToTable("ProjectUsers"); + }); + + modelBuilder.Entity("Core.Entities.Publication", b => + { + b.Property("PublicationID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("PublicationID")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Doi") + .HasColumnType("text"); + + b.Property("Issue") + .HasColumnType("text"); + + b.Property("Journal") + .IsRequired() + .HasColumnType("text"); + + b.Property("Notes") + .HasColumnType("text"); + + b.Property("Pages") + .HasColumnType("text"); + + b.Property("ProjectID") + .HasColumnType("integer"); + + b.Property("SourceAuthor") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("Url") + .HasColumnType("text"); + + b.Property("Volume") + .HasColumnType("text"); + + b.Property("Year") + .IsRequired() + .HasColumnType("integer"); + + b.HasKey("PublicationID"); + + b.HasIndex("ProjectID"); + + b.ToTable("Publications"); + }); + + modelBuilder.Entity("Core.Entities.Tag", b => + { + b.Property("TagID") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("TagID")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("TagID"); + + b.ToTable("Tag"); + }); + + modelBuilder.Entity("Core.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("Bio") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("DateCreated") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LastOnline") + .HasColumnType("timestamp with time zone"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("ReceiveCommentReplyEmails") + .HasColumnType("boolean"); + + b.Property("RegistrationSurvey") + .HasColumnType("text"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Core.Entities.UserUser", b => + { + b.Property("UserID") + .HasColumnType("integer") + .HasColumnOrder(1); + + b.Property("FollowerID") + .HasColumnType("integer") + .HasColumnOrder(2); + + b.HasKey("UserID", "FollowerID"); + + b.HasIndex("FollowerID"); + + b.ToTable("UserUsers"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = 1, + ConcurrencyStamp = "d7c6278e-3f78-4db6-8832-aa41ea9fd62c", + Name = "Admin", + NormalizedName = "ADMIN" + }, + new + { + Id = 2, + ConcurrencyStamp = "7383e31e-a884-4cd6-82f7-a6287b4e30a5", + Name = "Customer", + NormalizedName = "CUSTOMER" + }, + new + { + Id = 3, + ConcurrencyStamp = "398ef1f2-6b82-45f2-9601-ed2129f197e9", + Name = "Moderator", + NormalizedName = "MODERATOR" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("RoleId") + .HasColumnType("integer"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("integer"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Analysim.Core.Entities.BlobFileContent", b => + { + b.HasOne("Core.Entities.BlobFile", "BlobFile") + .WithMany("BlobFileContents") + .HasForeignKey("BlobFileID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlobFile"); + }); + + modelBuilder.Entity("Analysim.Core.Entities.NotebookContent", b => + { + b.HasOne("Core.Entities.Notebook", "Notebook") + .WithMany("NotebookContents") + .HasForeignKey("NotebookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Notebook"); + }); + + modelBuilder.Entity("Core.Entities.BlobFile", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("BlobFiles") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Core.Entities.User", "User") + .WithMany("BlobFiles") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.Notebook", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("Notebooks") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Core.Entities.ObservableNotebookDataset", b => + { + b.HasOne("Core.Entities.Notebook", "notebook") + .WithMany("observableNotebookDatasets") + .HasForeignKey("NotebookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("notebook"); + }); + + modelBuilder.Entity("Core.Entities.ProjectComment", b => + { + b.HasOne("Core.Entities.ProjectComment", "ParentComment") + .WithMany("Replies") + .HasForeignKey("ParentCommentID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectComments") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.ProjectLog", "ProjectLog") + .WithMany("Comments") + .HasForeignKey("ProjectLogID") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Core.Entities.User", "User") + .WithMany("ProjectComments") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ParentComment"); + + b.Navigation("Project"); + + b.Navigation("ProjectLog"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentFlag", b => + { + b.HasOne("Core.Entities.ProjectComment", "ProjectComment") + .WithMany("CommentFlags") + .HasForeignKey("CommentID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("CommentFlags") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProjectComment"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectCommentLike", b => + { + b.HasOne("Core.Entities.ProjectComment", "ProjectComment") + .WithMany("CommentLikes") + .HasForeignKey("CommentID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("CommentLikes") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProjectComment"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectLog", b => + { + b.HasOne("Core.Entities.BlobFile", "BlobFile") + .WithMany() + .HasForeignKey("BlobFileID") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectLogs") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.Notebook", "ReferencedNotebook") + .WithMany() + .HasForeignKey("ReferencedNotebookID") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Core.Entities.User", "User") + .WithMany("ProjectLogs") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("BlobFile"); + + b.Navigation("Project"); + + b.Navigation("ReferencedNotebook"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.ProjectTag", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectTags") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.Tag", "Tag") + .WithMany("ProjectTags") + .HasForeignKey("TagID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("Core.Entities.ProjectUser", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("ProjectUsers") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("ProjectUsers") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Core.Entities.Publication", b => + { + b.HasOne("Core.Entities.Project", "Project") + .WithMany("Publications") + .HasForeignKey("ProjectID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Project"); + }); + + modelBuilder.Entity("Core.Entities.UserUser", b => + { + b.HasOne("Core.Entities.User", "Follower") + .WithMany("Following") + .HasForeignKey("FollowerID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Core.Entities.User", "User") + .WithMany("Followers") + .HasForeignKey("UserID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Follower"); + + b.Navigation("User"); + }); + + 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("Core.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Core.Entities.User", 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("Core.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Core.Entities.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Core.Entities.BlobFile", b => + { + b.Navigation("BlobFileContents"); + }); + + modelBuilder.Entity("Core.Entities.Notebook", b => + { + b.Navigation("NotebookContents"); + + b.Navigation("observableNotebookDatasets"); + }); + + modelBuilder.Entity("Core.Entities.Project", b => + { + b.Navigation("BlobFiles"); + + b.Navigation("Notebooks"); + + b.Navigation("ProjectComments"); + + b.Navigation("ProjectLogs"); + + b.Navigation("ProjectTags"); + + b.Navigation("ProjectUsers"); + + b.Navigation("Publications"); + }); + + modelBuilder.Entity("Core.Entities.ProjectComment", b => + { + b.Navigation("CommentFlags"); + + b.Navigation("CommentLikes"); + + b.Navigation("Replies"); + }); + + modelBuilder.Entity("Core.Entities.ProjectLog", b => + { + b.Navigation("Comments"); + }); + + modelBuilder.Entity("Core.Entities.Tag", b => + { + b.Navigation("ProjectTags"); + }); + + modelBuilder.Entity("Core.Entities.User", b => + { + b.Navigation("BlobFiles"); + + b.Navigation("CommentFlags"); + + b.Navigation("CommentLikes"); + + b.Navigation("Followers"); + + b.Navigation("Following"); + + b.Navigation("ProjectComments"); + + b.Navigation("ProjectLogs"); + + b.Navigation("ProjectUsers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Analysim.Infrastructure/Migrations/20260520182711_AddVolIssuePagesToPublications.cs b/src/Analysim.Infrastructure/Migrations/20260520182711_AddVolIssuePagesToPublications.cs new file mode 100644 index 00000000..30aa3fa1 --- /dev/null +++ b/src/Analysim.Infrastructure/Migrations/20260520182711_AddVolIssuePagesToPublications.cs @@ -0,0 +1,105 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + public partial class AddVolIssuePagesToPublications : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Issue", + table: "Publications", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Pages", + table: "Publications", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Volume", + table: "Publications", + type: "text", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Content", + table: "ProjectComments", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 1, + column: "ConcurrencyStamp", + value: "d7c6278e-3f78-4db6-8832-aa41ea9fd62c"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 2, + column: "ConcurrencyStamp", + value: "7383e31e-a884-4cd6-82f7-a6287b4e30a5"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 3, + column: "ConcurrencyStamp", + value: "398ef1f2-6b82-45f2-9601-ed2129f197e9"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Issue", + table: "Publications"); + + migrationBuilder.DropColumn( + name: "Pages", + table: "Publications"); + + migrationBuilder.DropColumn( + name: "Volume", + table: "Publications"); + + migrationBuilder.AlterColumn( + name: "Content", + table: "ProjectComments", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 1, + column: "ConcurrencyStamp", + value: "f8accda8-f6e5-43e2-acdc-ec7489690c7e"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 2, + column: "ConcurrencyStamp", + value: "5acac352-55a0-4b09-bc33-6a734391391b"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 3, + column: "ConcurrencyStamp", + value: "67d7e55c-aee8-4fe1-8cb4-c89f670c3c24"); + } + } +} diff --git a/src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts b/src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts index b226a48c..41245edc 100644 --- a/src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts +++ b/src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts @@ -21,6 +21,8 @@ import { ModalDeleteReportsComponent } from './components/comments/modal-delete- import { ModalIgnoreReportsComponent } from './components/comments/modal-ignore-reports/modal-ignore-reports.component'; import { ProjectLogsComponent } from './components/project-logs/project-logs.component'; import { ExpiredLogItemComponent } from './components/project-logs/expired-log-item/expired-log-item.component'; +import { ModalDeleteLogComponent } from './components/project-logs/modal-delete-log/modal-delete-log.component'; +import { ModalRestoreLogComponent } from './components/project-logs/modal-restore-log/modal-restore-log.component'; @NgModule({ declarations: [ @@ -41,7 +43,9 @@ import { ExpiredLogItemComponent } from './components/project-logs/expired-log-i ModalDeleteReportsComponent, ModalIgnoreReportsComponent, ProjectLogsComponent, - ExpiredLogItemComponent + ExpiredLogItemComponent, + ModalDeleteLogComponent, + ModalRestoreLogComponent ], imports: [ CommonModule, diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.html index a085c2a5..07c7685a 100644 --- a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.html +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.html @@ -30,6 +30,14 @@
+ +
- \ No newline at end of file + + + + + + + + + + + \ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.scss index 905ff0dd..35b6e413 100644 --- a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.scss +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.scss @@ -4,7 +4,7 @@ .expired-log-row { display: grid; - grid-template-columns: 1.5fr .75fr .75fr .5fr .5fr .5fr .25fr; + grid-template-columns: 1.5fr .75fr .75fr .5fr .5fr .5fr .5fr; gap: var(--space-3); align-items: center; padding: var(--space-1) var(--space-3); @@ -63,6 +63,7 @@ .actions-col { display: flex; justify-content: flex-end; + gap: var(--space-2); } @media (max-width: 900px) { diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.ts index 93f7b29d..45c45084 100644 --- a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.ts +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/expired-log-item/expired-log-item.component.ts @@ -1,4 +1,5 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, TemplateRef, ViewChild } from '@angular/core'; +import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal'; import { ExpiredProjectLog } from 'src/app/interfaces/expired-project-log'; @Component({ @@ -8,15 +9,56 @@ import { ExpiredProjectLog } from 'src/app/interfaces/expired-project-log'; }) export class ExpiredLogItemComponent implements OnInit { @Input() log!: ExpiredProjectLog; - @Output() deleteLog = new EventEmitter(); + @Output() onReload = new EventEmitter(); - constructor() { } + // Modals + @ViewChild('restoreModal') restoreModal: TemplateRef; + @ViewChild('deleteModal') deleteModal: TemplateRef; + + // Modal Refs + restoreModalRef: BsModalRef; + deleteModalRef: BsModalRef; + + isDeleted: boolean; + isDeleting: boolean; + isRestoring: boolean; + + constructor(private modalService: BsModalService) {} ngOnInit(): void { } + // Delete + onDelete(): void { - this.deleteLog.emit(this.log.logID); + this.toggleModalDelete(); + this.isDeleting = true; + } + + onHandleSuccessfulDelete(): void { + this.isDeleted = true; + this.isDeleting = false; + this.onReload.emit(); + } + + toggleModalDelete() { + this.deleteModalRef = this.modalService.show(this.deleteModal) + } + + // Restore + + onRestore(): void { + this.toggleModalRestore(); + this.isRestoring = true; + } + + onHandleSuccessfulRestore(): void { + this.isRestoring = false; + this.onReload.emit(); + } + + toggleModalRestore() { + this.restoreModalRef = this.modalService.show(this.restoreModal) } } diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.html new file mode 100644 index 00000000..eecb8052 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.html @@ -0,0 +1,48 @@ + diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.scss new file mode 100644 index 00000000..e4caf7ff --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.scss @@ -0,0 +1,15 @@ +.modal-confirm{ + text-align: center; + font-weight: 700; + font-size: large; + padding: 0; + padding-bottom: 1rem; + margin: 0; +} + +.modal-warning{ + text-align: center; + font-size: small; + padding: 0; + margin: 0; +} \ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.spec.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.spec.ts new file mode 100644 index 00000000..9d6c2a09 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalDeleteLogComponent } from './modal-delete-log.component'; + +describe('ModalDeleteLogComponent', () => { + let component: ModalDeleteLogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ModalDeleteLogComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ModalDeleteLogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.ts new file mode 100644 index 00000000..9eb76da3 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-delete-log/modal-delete-log.component.ts @@ -0,0 +1,44 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { BsModalRef } from 'ngx-bootstrap/modal'; +import { ProjectService } from 'src/app/services/project.service'; + +@Component({ + selector: 'app-modal-delete-log', + templateUrl: './modal-delete-log.component.html', + styleUrls: ['./modal-delete-log.component.scss'], +}) +export class ModalDeleteLogComponent implements OnInit { + @Input() deleteModalRef: BsModalRef; + @Input() logID: number; + + @Output() onSuccessfulDelete = new EventEmitter(); + @Output() onCancelDelete = new EventEmitter(); + + errorResult: String; + errorStatusAlert = false; + + constructor(private projectService: ProjectService) {} + + ngOnInit(): void {} + + onDelete(): void { + this.projectService.deleteExpiredProjectLog(this.logID).subscribe({ + next: (result) => { + console.log('Deleted log:', this.logID); + this.onSuccessfulDelete.emit(); + this.deleteModalRef.hide(); + }, + error: (error) => { + this.errorStatusAlert = true; + this.errorResult = + 'Error: unable to delete log, please contact developers for assistance'; + console.log(error); + }, + }); + } + + closeModal() { + this.onCancelDelete.emit(); + this.deleteModalRef.hide(); + } +} diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.html new file mode 100644 index 00000000..b072d17d --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.html @@ -0,0 +1,48 @@ + diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.scss new file mode 100644 index 00000000..e4caf7ff --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.scss @@ -0,0 +1,15 @@ +.modal-confirm{ + text-align: center; + font-weight: 700; + font-size: large; + padding: 0; + padding-bottom: 1rem; + margin: 0; +} + +.modal-warning{ + text-align: center; + font-size: small; + padding: 0; + margin: 0; +} \ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.spec.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.spec.ts new file mode 100644 index 00000000..dec869bb --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalRestoreLogComponent } from './modal-restore-log.component'; + +describe('ModalRestoreLogComponent', () => { + let component: ModalRestoreLogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ModalRestoreLogComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ModalRestoreLogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.ts new file mode 100644 index 00000000..7e4e21bd --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/modal-restore-log/modal-restore-log.component.ts @@ -0,0 +1,43 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { BsModalRef } from 'ngx-bootstrap/modal'; +import { ProjectService } from 'src/app/services/project.service'; + +@Component({ + selector: 'app-modal-restore-log', + templateUrl: './modal-restore-log.component.html', + styleUrls: ['./modal-restore-log.component.scss'] +}) +export class ModalRestoreLogComponent implements OnInit { + @Input() restoreModalRef: BsModalRef + @Input() logID: number; + + @Output() onSuccessfulRestore = new EventEmitter(); + @Output() onCancelRestore = new EventEmitter(); + + errorResult: String; + errorStatusAlert = false; + + constructor(private projectService: ProjectService) { } + + ngOnInit(): void {} + + onRestore(): void { + this.projectService.repostProjectLog(this.logID).subscribe({ + next: (result) => { + console.log('Restored log', this.logID); + this.onSuccessfulRestore.emit(); + this.restoreModalRef.hide(); + }, + error: (error) => { + this.errorStatusAlert = true; + this.errorResult = "Error: unable to restore log, please contact developers for assistance"; + console.log(error); + }, + }); + } + + closeModal() { + this.onCancelRestore.emit(); + this.restoreModalRef.hide(); + } +} diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.html index bfd207d0..55c7ed95 100644 --- a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.html +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.html @@ -40,7 +40,7 @@

Expired Logs

diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.scss index 92280443..0fc3044f 100644 --- a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.scss +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.scss @@ -11,7 +11,7 @@ .expired-log-header { display: grid; - grid-template-columns: 1.5fr .75fr .75fr .5fr .5fr .5fr .25fr; + grid-template-columns: 1.5fr .75fr .75fr .5fr .5fr .5fr .5fr; gap: var(--space-3); align-items: center; padding: var(--space-1) var(--space-3); @@ -24,7 +24,6 @@ .actions-col { display: flex; - justify-content: flex-end; } .empty-set{ diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.ts index 044e0485..a9af5f1d 100644 --- a/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.ts +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/project-logs/project-logs.component.ts @@ -34,18 +34,4 @@ export class ProjectLogsComponent implements OnInit { }, }); } - - deleteExpiredLog(logID: number){ - this.projectService.deleteExpiredProjectLog(logID).subscribe({ - next: (result) => { - console.log('Deleted project log', logID); - this.loadLogs(); - }, - error: (error) => { - this.errorStatusAlert = true; - this.errorResult = "Error: unable to delete project log, please try again later"; - console.log(error); - }, - }); - } } diff --git a/src/Analysim.Web/ClientApp/src/app/interfaces/project-log.ts b/src/Analysim.Web/ClientApp/src/app/interfaces/project-log.ts index 51f9b08f..00b9b70a 100644 --- a/src/Analysim.Web/ClientApp/src/app/interfaces/project-log.ts +++ b/src/Analysim.Web/ClientApp/src/app/interfaces/project-log.ts @@ -1,15 +1,24 @@ -import { ProjectComment } from "./project-comment"; - export interface ProjectLog { logID: number; userID: number; authorName: string; projectID: number; title: string | null; + referencedNotebookID: number | null; + referencedNotebookVersion: number | null; + referencedNotebook: ReferencedNotebook | null; image: string | null; content: string; isDeleted: boolean; createdAt: string; updatedAt: string; commentCount: number; +} + +export interface ReferencedNotebook { + notebookID: number; + name: string; + extension: string; + directory: string; + type: string; } \ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/interfaces/publication.ts b/src/Analysim.Web/ClientApp/src/app/interfaces/publication.ts index 066a05c8..9860e709 100644 --- a/src/Analysim.Web/ClientApp/src/app/interfaces/publication.ts +++ b/src/Analysim.Web/ClientApp/src/app/interfaces/publication.ts @@ -6,6 +6,9 @@ export interface Publication { doi: string; sourceAuthor: string; year: number; + volume: string; + issue: string; + pages: string; notes: string; createdAt: string; } \ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-log/project-log-item/project-log-item.component.html b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-log/project-log-item/project-log-item.component.html index 1d4cfad9..5e6ac3ca 100644 --- a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-log/project-log-item/project-log-item.component.html +++ b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-log/project-log-item/project-log-item.component.html @@ -73,9 +73,17 @@


- @@ -134,6 +142,57 @@


+ +
+
+ Reference Notebook +
+ +
+
+ + + +
+ +
+ + + +
+
+
+ +
+ - - - - - - - - - - - - +
+
+ + + + + + + + + + + +
+ +
+ +
+ + + + + + + + +
+ +
+ +
+ + + + + + + + +
diff --git a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.scss b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.scss index dca488f4..981ecf5c 100644 --- a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.scss +++ b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.scss @@ -19,7 +19,7 @@ grid-template-columns: max-content 1fr; column-gap: var(--space-3); align-items: center; - margin-bottom: 1rem; + margin-bottom: .5rem; } .publication-form { @@ -39,6 +39,7 @@ width: 100%; min-width: 0; color: black !important; + padding: 0 var(--space-2); } .form-header { @@ -47,4 +48,17 @@ .error-center{ text-align: center; -} \ No newline at end of file +} + +.details-area { + display: flex; + flex-direction: row; + gap: 1rem var(--space-3); + margin-top: .5rem; + margin-bottom: .5rem; + align-items: center; +} + +// .control-sm{ +// width: 25px !important; +// } \ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.ts b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.ts index d1049180..adee8afe 100644 --- a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.ts +++ b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/modal-edit-publication/modal-edit-publication.component.ts @@ -35,6 +35,9 @@ export class ModalEditPublicationComponent implements OnInit { doi: FormControl; sourceAuthor: FormControl; year: FormControl; + volume: FormControl; + issue: FormControl; + pages: FormControl; journal: FormControl; notes: FormControl; isLoading: boolean = false; @@ -57,6 +60,9 @@ export class ModalEditPublicationComponent implements OnInit { this.doi = new FormControl(ep ? (ep.doi ? ep.doi : '') : ''); this.sourceAuthor = new FormControl(ep ? (ep.sourceAuthor ? ep.sourceAuthor : '') : ''); this.year = new FormControl(ep ? (ep.year ? ep.year : '') : ''); + this.volume = new FormControl(ep ? (ep.volume ? ep.volume : '') : ''); + this.issue = new FormControl(ep ? (ep.issue ? ep.issue : '') : ''); + this.pages = new FormControl(ep ? (ep.pages ? ep.pages : '') : ''); this.journal = new FormControl(ep ? (ep.journal ? ep.journal : '') : ''); this.notes = new FormControl(ep ? (ep.notes ? ep.notes : '') : ''); @@ -68,6 +74,9 @@ export class ModalEditPublicationComponent implements OnInit { doi: this.doi, sourceAuthor: this.sourceAuthor, year: this.year, + volume: this.volume, + issue: this.issue, + pages: this.pages, notes: this.notes, }); } @@ -156,6 +165,9 @@ export class ModalEditPublicationComponent implements OnInit { if (pub.title) formData.append('title', pub.title); if (pub.url) formData.append('url', pub.url); if (pub.doi) formData.append('doi', pub.doi); + if (pub.volume) formData.append('volume', pub.volume); + if (pub.issue) formData.append('issue', pub.issue); + if (pub.pages) formData.append('pages', pub.pages); if (pub.notes) formData.append('notes', pub.notes); return formData; diff --git a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/publication-item/publication-item.component.html b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/publication-item/publication-item.component.html index f245aa74..33ba66af 100644 --- a/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/publication-item/publication-item.component.html +++ b/src/Analysim.Web/ClientApp/src/app/projects/project-overview/project-overview-view/project-publication/publication-item/publication-item.component.html @@ -1,9 +1,19 @@
-
-

- {{ publication.sourceAuthor }} ({{publication.year}}). {{ publication.title }}. {{ publication.journal }} -

-
+
+
+ {{ publication.sourceAuthor }} ({{ publication.year }}). + {{ publication.title }}. + {{ publication.journal }} + + + · + Vol. {{ publication.volume }} + (Issue {{ publication.issue }}) + : + pp. {{ publication.pages }} + +
+
- -