From d60d48acf7ef4390a0e4c677498c9c3cc55169a3 Mon Sep 17 00:00:00 2001 From: uday biswas Date: Wed, 4 Jun 2025 02:35:24 +0530 Subject: [PATCH 1/2] Swagger ui delete project api resolved --- src/Analysim.Web/ClientApp/package-lock.json | 75 +++++++++++++------- src/Analysim.Web/Startup.cs | 35 ++++++++- 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/src/Analysim.Web/ClientApp/package-lock.json b/src/Analysim.Web/ClientApp/package-lock.json index 361c055..e82d36b 100644 --- a/src/Analysim.Web/ClientApp/package-lock.json +++ b/src/Analysim.Web/ClientApp/package-lock.json @@ -15757,13 +15757,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.1.tgz", "integrity": "sha512-f1G1WGDXEU/RN1TWAxBPQgQudtLnLQPyiWdtypkPC+mVYNKFKH/HYXSxH4MVNqwF8M0eDsoiU7HumJHCg/L/jg==", - "dev": true + "dev": true, + "requires": {} }, "@csstools/selector-specificity": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.1.tgz", "integrity": "sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA==", - "dev": true + "dev": true, + "requires": {} }, "@discoveryjs/json-ext": { "version": "0.5.7", @@ -15932,7 +15934,8 @@ "version": "14.0.3", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.0.3.tgz", "integrity": "sha512-PwvgCeY7mbijazovpA0ggeo81A3yzwOb8AfVD3yfGT15Z2qnEVyL+05Tj6ttRTngceF3gsERamFcB6lRKdcjdw==", - "dev": true + "dev": true, + "requires": {} }, "@nodelib/fs.scandir": { "version": "2.1.5", @@ -16834,7 +16837,8 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true + "dev": true, + "requires": {} }, "acorn-walk": { "version": "7.2.0", @@ -17234,7 +17238,8 @@ "bootstrap": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", - "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==" + "integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", + "requires": {} }, "bootstrap-icons": { "version": "1.11.3", @@ -17921,7 +17926,8 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "dev": true + "dev": true, + "requires": {} }, "css-select": { "version": "4.3.0", @@ -19682,7 +19688,8 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true + "dev": true, + "requires": {} }, "ieee754": { "version": "1.2.1", @@ -20487,7 +20494,8 @@ "ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} } } }, @@ -20711,7 +20719,8 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz", "integrity": "sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==", - "dev": true + "dev": true, + "requires": {} }, "karma-source-map-support": { "version": "1.4.0", @@ -21108,7 +21117,8 @@ "marked-highlight": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/marked-highlight/-/marked-highlight-2.1.3.tgz", - "integrity": "sha512-t35JWm2u8HanOJ+gSJBAYQ0Jgr3vy+gl7ORAXN8bSEQFHl5FYXH0A7YXVMrfhmKaSuBSy6LidXECn3U9Qv/dHA==" + "integrity": "sha512-t35JWm2u8HanOJ+gSJBAYQ0Jgr3vy+gl7ORAXN8bSEQFHl5FYXH0A7YXVMrfhmKaSuBSy6LidXECn3U9Qv/dHA==", + "requires": {} }, "media-typer": { "version": "0.3.0", @@ -22270,13 +22280,15 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "dev": true + "dev": true, + "requires": {} }, "postcss-gap-properties": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz", "integrity": "sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-image-set-function": { "version": "4.0.6", @@ -22302,7 +22314,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-lab-function": { "version": "4.2.0", @@ -22329,19 +22342,22 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "dev": true + "dev": true, + "requires": {} }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-extract-imports": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true + "dev": true, + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.5", @@ -22392,13 +22408,15 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz", "integrity": "sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==", - "dev": true + "dev": true, + "requires": {} }, "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "dev": true + "dev": true, + "requires": {} }, "postcss-place": { "version": "7.0.4", @@ -22475,7 +22493,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "dev": true + "dev": true, + "requires": {} }, "postcss-selector-not": { "version": "5.0.0", @@ -23036,7 +23055,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -23588,7 +23608,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", - "dev": true + "dev": true, + "requires": {} }, "stylus": { "version": "0.57.0", @@ -23748,7 +23769,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -24168,7 +24190,8 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "json-schema-traverse": { "version": "0.4.1", @@ -24268,7 +24291,8 @@ "version": "8.8.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz", "integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==", - "dev": true + "dev": true, + "requires": {} } } }, @@ -24409,7 +24433,8 @@ "version": "8.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", - "dev": true + "dev": true, + "requires": {} }, "xml-name-validator": { "version": "3.0.0", diff --git a/src/Analysim.Web/Startup.cs b/src/Analysim.Web/Startup.cs index 5cf7837..8d00382 100644 --- a/src/Analysim.Web/Startup.cs +++ b/src/Analysim.Web/Startup.cs @@ -28,7 +28,40 @@ public void ConfigureServices(IServiceCollection services) services.ConfigureAuthorization(); - services.AddSwaggerGen(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo + { + Title = "AnalySim API", + Version = "v1" + }); + + c.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme + { + Name = "Authorization", + Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey, + Scheme = "Bearer", + BearerFormat = "JWT", + In = Microsoft.OpenApi.Models.ParameterLocation.Header, + Description = "Enter 'Bearer' followed by your JWT token (e.g. 'Bearer eyJhbGci...')" + }); + + c.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement + { + { + new Microsoft.OpenApi.Models.OpenApiSecurityScheme + { + Reference = new Microsoft.OpenApi.Models.OpenApiReference + { + Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + new string[] {} + } + }); + }); + services.ConfigureIdentity(); From 18542111789ddf59507a6bd521d0e940dbfb23e5 Mon Sep 17 00:00:00 2001 From: uday biswas Date: Wed, 25 Jun 2025 01:06:36 +0530 Subject: [PATCH 2/2] Admin section implemented --- src/Analysim.Core/Entities/Notebook.cs | 3 + .../ApplicationDbContextModelSnapshot.cs | 10 +- ...0250622230436_AddNotebookRoute.Designer.cs | 766 ++++++++++++++++++ .../20250622230436_AddNotebookRoute.cs | 68 ++ src/Analysim.Web/Analysim.Web.csproj | 1 + .../src/app/admin/admin-routing.module.ts | 29 + .../src/app/admin/admin.component.html | 19 + .../src/app/admin/admin.component.scss | 0 .../src/app/admin/admin.component.ts | 8 + .../ClientApp/src/app/admin/admin.module.ts | 26 + .../datasets/datasets.component.html | 31 + .../datasets/datasets.component.scss | 0 .../datasets/datasets.component.spec.ts | 23 + .../components/datasets/datasets.component.ts | 29 + .../admin-notebook-item.component.html | 1 + .../admin-notebook-item.component.scss | 0 .../admin-notebook-item.component.spec.ts | 23 + .../admin-notebook-item.component.ts | 15 + .../notebooks/notebooks.component.html | 36 + .../notebooks/notebooks.component.scss | 0 .../notebooks/notebooks.component.spec.ts | 23 + .../notebooks/notebooks.component.ts | 35 + .../projects/projects.component.html | 35 + .../projects/projects.component.scss | 0 .../projects/projects.component.spec.ts | 23 + .../components/projects/projects.component.ts | 30 + .../components/users/users.component.html | 28 + .../components/users/users.component.scss | 0 .../components/users/users.component.spec.ts | 23 + .../admin/components/users/users.component.ts | 30 + .../ClientApp/src/app/app-routing.module.ts | 1 + .../ClientApp/src/app/interfaces/notebook.ts | 3 +- .../src/app/services/project.service.ts | 30 + .../Controllers/ProjectController.cs | 45 +- 34 files changed, 1385 insertions(+), 9 deletions(-) create mode 100644 src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.Designer.cs create mode 100644 src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.cs create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/admin-routing.module.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/admin.component.html create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/admin.component.scss create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/admin.component.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.html create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.scss create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.spec.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.html create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.scss create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.spec.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.html create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.scss create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.spec.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.html create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.scss create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.spec.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.html create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.scss create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.spec.ts create mode 100644 src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.ts diff --git a/src/Analysim.Core/Entities/Notebook.cs b/src/Analysim.Core/Entities/Notebook.cs index 93c63a4..9fd2d78 100644 --- a/src/Analysim.Core/Entities/Notebook.cs +++ b/src/Analysim.Core/Entities/Notebook.cs @@ -23,6 +23,9 @@ public class Notebook [Required(ErrorMessage = "Notebook Name is a required field.")] public string Name { get; set; } + [Required(ErrorMessage = "Route name is a required field.")] + public string Route { get; set; } + [Required(ErrorMessage = "Notebook Extension is a required field.")] public string Extension { get; set; } diff --git a/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 985943c..ee014be 100644 --- a/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Analysim.Infrastructure/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -153,6 +153,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ProjectID") .HasColumnType("integer"); + b.Property("Route") + .IsRequired() + .HasColumnType("text"); + b.Property("Size") .HasColumnType("integer"); @@ -424,21 +428,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = 1, - ConcurrencyStamp = "b2996c18-b35e-4568-b061-1c3c2027543b", + ConcurrencyStamp = "ebd0169f-30be-4ab7-9fb9-038a9de20efb", Name = "Admin", NormalizedName = "ADMIN" }, new { Id = 2, - ConcurrencyStamp = "90f1b211-a965-4941-b973-71cd8a653cd7", + ConcurrencyStamp = "371acb40-c941-4714-9c2c-bc9de9bff144", Name = "Customer", NormalizedName = "CUSTOMER" }, new { Id = 3, - ConcurrencyStamp = "21584cfd-491d-4638-9753-5f9ec755712f", + ConcurrencyStamp = "7ee39326-5034-4323-85cc-6da801861458", Name = "Moderator", NormalizedName = "MODERATOR" }); diff --git a/src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.Designer.cs b/src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.Designer.cs new file mode 100644 index 0000000..2df245c --- /dev/null +++ b/src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.Designer.cs @@ -0,0 +1,766 @@ +// +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("20250622230436_AddNotebookRoute")] + partial class AddNotebookRoute + { + 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.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.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 = "ebd0169f-30be-4ab7-9fb9-038a9de20efb", + Name = "Admin", + NormalizedName = "ADMIN" + }, + new + { + Id = 2, + ConcurrencyStamp = "371acb40-c941-4714-9c2c-bc9de9bff144", + Name = "Customer", + NormalizedName = "CUSTOMER" + }, + new + { + Id = 3, + ConcurrencyStamp = "7ee39326-5034-4323-85cc-6da801861458", + 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.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.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("ProjectTags"); + + b.Navigation("ProjectUsers"); + }); + + modelBuilder.Entity("Core.Entities.Tag", b => + { + b.Navigation("ProjectTags"); + }); + + modelBuilder.Entity("Core.Entities.User", b => + { + b.Navigation("BlobFiles"); + + b.Navigation("Followers"); + + b.Navigation("Following"); + + b.Navigation("ProjectUsers"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.cs b/src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.cs new file mode 100644 index 0000000..6fc934b --- /dev/null +++ b/src/Analysim.Infrastructure/Migrations/20250622230436_AddNotebookRoute.cs @@ -0,0 +1,68 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Infrastructure.Migrations +{ + public partial class AddNotebookRoute : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Route", + table: "Notebook", + type: "text", + nullable: false, + defaultValue: ""); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 1, + column: "ConcurrencyStamp", + value: "ebd0169f-30be-4ab7-9fb9-038a9de20efb"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 2, + column: "ConcurrencyStamp", + value: "371acb40-c941-4714-9c2c-bc9de9bff144"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 3, + column: "ConcurrencyStamp", + value: "7ee39326-5034-4323-85cc-6da801861458"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Route", + table: "Notebook"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 1, + column: "ConcurrencyStamp", + value: "b2996c18-b35e-4568-b061-1c3c2027543b"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 2, + column: "ConcurrencyStamp", + value: "90f1b211-a965-4941-b973-71cd8a653cd7"); + + migrationBuilder.UpdateData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: 3, + column: "ConcurrencyStamp", + value: "21584cfd-491d-4638-9753-5f9ec755712f"); + } + } +} diff --git a/src/Analysim.Web/Analysim.Web.csproj b/src/Analysim.Web/Analysim.Web.csproj index 393ae74..85d9912 100644 --- a/src/Analysim.Web/Analysim.Web.csproj +++ b/src/Analysim.Web/Analysim.Web.csproj @@ -48,6 +48,7 @@ + diff --git a/src/Analysim.Web/ClientApp/src/app/admin/admin-routing.module.ts b/src/Analysim.Web/ClientApp/src/app/admin/admin-routing.module.ts new file mode 100644 index 0000000..8c21719 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/admin-routing.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { AdminComponent } from './admin.component'; +import { AuthGuardService } from '../guards/auth-guard.service'; +import { NotebooksComponent } from './components/notebooks/notebooks.component'; +import { UsersComponent } from './components/users/users.component'; +import { DatasetsComponent } from './components/datasets/datasets.component'; +import { ProjectsComponent } from './components/projects/projects.component'; + +const routes: Routes = [ + { + path: '', + component: AdminComponent, + canActivate: [AuthGuardService], + children: [ + { path: 'notebooks', component: NotebooksComponent }, + { path: 'users', component: UsersComponent }, + { path: 'datasets', component: DatasetsComponent }, + { path: 'projects', component: ProjectsComponent }, + { path: '', redirectTo: 'notebooks', pathMatch: 'full' } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AdminRoutingModule { } diff --git a/src/Analysim.Web/ClientApp/src/app/admin/admin.component.html b/src/Analysim.Web/ClientApp/src/app/admin/admin.component.html new file mode 100644 index 0000000..933d886 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/admin.component.html @@ -0,0 +1,19 @@ + +
+ +
\ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/admin/admin.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/admin.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Analysim.Web/ClientApp/src/app/admin/admin.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/admin.component.ts new file mode 100644 index 0000000..9679714 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/admin.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-admin', + templateUrl: './admin.component.html', + styleUrls: ['./admin.component.scss'] +}) +export class AdminComponent { } \ No newline at end of file diff --git a/src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts b/src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts new file mode 100644 index 0000000..d89d13d --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/admin.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { AdminRoutingModule } from './admin-routing.module'; +import { AdminComponent } from './admin.component'; +import { NotebooksComponent } from './components/notebooks/notebooks.component'; +import { UsersComponent } from './components/users/users.component'; +import { DatasetsComponent } from './components/datasets/datasets.component'; +import { ProjectsComponent } from './components/projects/projects.component'; +import { AdminNotebookItemComponent } from './components/notebooks/admin-notebook-item/admin-notebook-item.component'; + +@NgModule({ + declarations: [ + AdminComponent, + NotebooksComponent, + UsersComponent, + DatasetsComponent, + ProjectsComponent, + AdminNotebookItemComponent + ], + imports: [ + CommonModule, + AdminRoutingModule + ] +}) +export class AdminModule { } diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.html new file mode 100644 index 0000000..99d74ff --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.html @@ -0,0 +1,31 @@ + +
+
+
All Datasets
+
+
+ + + + + + + + + + + + + + + + + +
NameDirectorySizeDate Created
{{d.name}}{{d.extension}}{{d.directory || '/'}}{{d.size | number}} bytes{{d.dateCreated | date:'short'}}
+ +
+
Loading… +
+
{{ error }}
+
+
diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.spec.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.spec.ts new file mode 100644 index 0000000..f4e6ba1 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DatasetsComponent } from './datasets.component'; + +describe('DatasetsComponent', () => { + let component: DatasetsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DatasetsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(DatasetsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.ts new file mode 100644 index 0000000..fd18c30 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/datasets/datasets.component.ts @@ -0,0 +1,29 @@ +import { Component, OnInit } from '@angular/core'; +import { ProjectService } from 'src/app/services/project.service'; +import { BlobFile } from '../../../interfaces/blob-file'; + +@Component({ + selector: 'app-datasets', + templateUrl: './datasets.component.html', + styleUrls: ['./datasets.component.scss'] +}) +export class DatasetsComponent implements OnInit { + datasets: BlobFile[] = []; + loading = true; + error: string = null; + + constructor(private project: ProjectService) { } + + ngOnInit() { + this.project.getAllDatasets().subscribe({ + next: result => { + this.datasets = result; + this.loading = false; + }, + error: () => { + this.error = 'Failed to load datasets'; + this.loading = false; + } + }); + } +} diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.html new file mode 100644 index 0000000..b06904b --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.html @@ -0,0 +1 @@ +

admin-notebook-item works!

diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.spec.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.spec.ts new file mode 100644 index 0000000..ec41c3a --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AdminNotebookItemComponent } from './admin-notebook-item.component'; + +describe('AdminNotebookItemComponent', () => { + let component: AdminNotebookItemComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ AdminNotebookItemComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AdminNotebookItemComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.ts new file mode 100644 index 0000000..60ed75f --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/admin-notebook-item/admin-notebook-item.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-admin-notebook-item', + templateUrl: './admin-notebook-item.component.html', + styleUrls: ['./admin-notebook-item.component.scss'] +}) +export class AdminNotebookItemComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.html new file mode 100644 index 0000000..db96c24 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.html @@ -0,0 +1,36 @@ +
+

All Notebooks

+ +
+
Loading...
+
+ +
+ {{ error }} +
+ + + + + + + + + + + + + + + + + + +
#NameProjectDirectory
{{ i + 1 }}{{ nb.name+nb.extension }}{{ nb.route }} + {{ + nb.directory.startsWith('notebook') + ? nb.directory.slice('notebook'.length) + : nb.directory + }} +
+
diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.spec.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.spec.ts new file mode 100644 index 0000000..415b32f --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotebooksComponent } from './notebooks.component'; + +describe('NotebooksComponent', () => { + let component: NotebooksComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ NotebooksComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(NotebooksComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.ts new file mode 100644 index 0000000..e85aa72 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/notebooks/notebooks.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from '@angular/core'; +import { Notebook } from 'src/app/interfaces/notebook'; +import { ProjectService } from 'src/app/services/project.service'; + +@Component({ + selector: 'app-notebooks', + templateUrl: './notebooks.component.html', + styleUrls: ['./notebooks.component.scss'] +}) +export class NotebooksComponent implements OnInit { + notebooks: Notebook[] = []; + loading = false; + error: string | null = null; + + constructor(private projectService: ProjectService) { } + + ngOnInit(): void { + this.loadNotebooks(); + } + + loadNotebooks() { + this.loading = true; + this.error = null; + this.projectService.getAllNotebooks().subscribe({ + next: nbs => { + this.notebooks = nbs.filter(nb => nb.type !== 'folder'); + this.loading = false; + }, + error: err => { + this.error = err.message || 'Failed to load notebooks'; + this.loading = false; + } + }); + } +} diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.html new file mode 100644 index 0000000..002cafd --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.html @@ -0,0 +1,35 @@ + +
+
+
All Projects
+
+
+ + + + + + + + + + + + + + + + + + + + + +
IDNameDescriptionVisibilityDate CreatedLast Updated
{{ p.projectID }} {{p.route}}{{ p.description }}{{ p.visibility }}{{ p.dateCreated | date:'short' }}{{ p.lastUpdated | date:'short' }}
+ +
+
Loading… +
+
{{ error }}
+
+
diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.spec.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.spec.ts new file mode 100644 index 0000000..379f18b --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ProjectsComponent } from './projects.component'; + +describe('ProjectsComponent', () => { + let component: ProjectsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ProjectsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(ProjectsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.ts new file mode 100644 index 0000000..0141bef --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/projects/projects.component.ts @@ -0,0 +1,30 @@ +// src/app/admin/admin-projects/admin-projects.component.ts +import { Component, OnInit } from '@angular/core'; +import { ProjectService } from 'src/app/services/project.service'; +import { Project } from '../../../interfaces/project'; + +@Component({ + selector: 'app-projects', + templateUrl: './projects.component.html', + styleUrls: ['./projects.component.scss'] +}) +export class ProjectsComponent implements OnInit { + projects: Project[] = []; + loading = true; + error: string = null; + + constructor(private projectService: ProjectService) { } + + ngOnInit() { + this.projectService.getProjectList().subscribe({ + next: result => { + this.projects = result; + this.loading = false; + }, + error: () => { + this.error = 'Failed to load projects'; + this.loading = false; + } + }); + } +} diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.html b/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.html new file mode 100644 index 0000000..4dfe13c --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.html @@ -0,0 +1,28 @@ +
+
+
All Users
+
+
+ + + + + + + + + + + + + + + +
UsernameEmailDate Created
{{u.userName}}{{u.email}}{{u.dateCreated | date:'short'}}
+ +
+
Loading… +
+
{{ error }}
+
+
diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.scss b/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.spec.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.spec.ts new file mode 100644 index 0000000..80f23cd --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UsersComponent } from './users.component'; + +describe('UsersComponent', () => { + let component: UsersComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ UsersComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UsersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.ts b/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.ts new file mode 100644 index 0000000..ee2a5b8 --- /dev/null +++ b/src/Analysim.Web/ClientApp/src/app/admin/components/users/users.component.ts @@ -0,0 +1,30 @@ +import { Component, OnInit } from '@angular/core'; +import { AccountService } from 'src/app/services/account.service'; +import { User } from '../../../interfaces/user'; + +@Component({ + selector: 'app-users', + templateUrl: './users.component.html', + styleUrls: ['./users.component.scss'] +}) +export class UsersComponent implements OnInit { + users: User[] = []; + loading = true; + error: string = null; + + constructor(private account: AccountService) { } + + ngOnInit() { + this.account.getUserList() + .subscribe({ + next: result => { + this.users = result; + this.loading = false; + }, + error: err => { + this.error = 'Failed to load users'; + this.loading = false; + } + }); + } +} diff --git a/src/Analysim.Web/ClientApp/src/app/app-routing.module.ts b/src/Analysim.Web/ClientApp/src/app/app-routing.module.ts index de77c32..ffa5fb4 100644 --- a/src/Analysim.Web/ClientApp/src/app/app-routing.module.ts +++ b/src/Analysim.Web/ClientApp/src/app/app-routing.module.ts @@ -34,6 +34,7 @@ const routes: Routes = []; { path: 'dashboard', component: DashboardComponent }, { path: 'profile/:username', component: ProfileComponent }, { path: 'project', loadChildren: () => import('./projects/projects.module').then(m => m.ProjectsModule) }, + { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }, { path: '**', component: NotFoundComponent } // todo: Add verify page routing and component ])], diff --git a/src/Analysim.Web/ClientApp/src/app/interfaces/notebook.ts b/src/Analysim.Web/ClientApp/src/app/interfaces/notebook.ts index 1b42e1d..04230c0 100644 --- a/src/Analysim.Web/ClientApp/src/app/interfaces/notebook.ts +++ b/src/Analysim.Web/ClientApp/src/app/interfaces/notebook.ts @@ -26,5 +26,6 @@ export interface Notebook { projectID: number, type: string, directory: string, + route: string, observableNotebookDatasets: ObservableHQDataset[] -} \ No newline at end of file +} diff --git a/src/Analysim.Web/ClientApp/src/app/services/project.service.ts b/src/Analysim.Web/ClientApp/src/app/services/project.service.ts index 509ee12..210b32c 100644 --- a/src/Analysim.Web/ClientApp/src/app/services/project.service.ts +++ b/src/Analysim.Web/ClientApp/src/app/services/project.service.ts @@ -68,7 +68,9 @@ export class ProjectService { // Extra private urlGetUserList: string = this.baseUrl + "getuserlist/" private urlGetFileList: string = this.baseUrl + "getfilelist/" + private urlGetAllDatasetsList: string = this.baseUrl + "getAllDatasets/"; private urlGetNotebookList: string = this.baseUrl + "getNotebooks/"; + private urlGetAllNotebookList: string = this.baseUrl + "getAllNotebooks/"; private urlGetNotebook: string = this.baseUrl + "GetNotebook/"; private urlGetTagList: string = this.baseUrl + "gettaglist/" @@ -697,6 +699,20 @@ export class ProjectService { ) } + getAllDatasets(): Observable { + + return this.http.get(this.urlGetAllDatasetsList) + .pipe( + map(body => { + return body.result + }), + catchError(error => { + console.log(error) + return throwError(error) + }) + ) + } + getNotebooks(projectID: number, directory: string): Observable { return this.http.get(this.urlGetNotebookList + projectID + "/" + directory) .pipe( @@ -711,6 +727,20 @@ export class ProjectService { ) } + getAllNotebooks(): Observable { + return this.http.get(this.urlGetAllNotebookList) + .pipe( + map(body => { + console.log(body) + return body.result + }), + catchError(error => { + console.log(error) + return throwError(error) + }) + ) + } + getNotebook(notebookID: number) { return this.http.get(this.urlGetNotebook + notebookID) .pipe( diff --git a/src/Analysim.Web/Controllers/ProjectController.cs b/src/Analysim.Web/Controllers/ProjectController.cs index be5626a..4aff8f9 100644 --- a/src/Analysim.Web/Controllers/ProjectController.cs +++ b/src/Analysim.Web/Controllers/ProjectController.cs @@ -287,6 +287,20 @@ public async Task GetNotebook([FromRoute] int notebookID) } } + [HttpGet("[action]")] + public IActionResult GetAllNotebooks() + { + var notebooks = _dbContext.Notebook + .Include(n => n.observableNotebookDatasets) + .ToList(); + + return Ok(new + { + result = notebooks, + message = "All notebooks retrieved" + }); + } + [HttpGet("[action]/{notebookID}")] public async Task GetNotebookVersions([FromRoute] int notebookID) { @@ -620,7 +634,8 @@ await _dbContext.ProjectTags.AddRangeAsync( DateCreated = DateTime.UtcNow, LastModified = DateTime.UtcNow, ProjectID = newProject.ProjectID, - type = "new" + type = "new", + Route = user.UserName + "/" + newProject.Name }; await _dbContext.Notebook.AddAsync(readmeNotebook); @@ -966,7 +981,8 @@ public async Task UploadNotebook([FromForm] ProjectNotebookUpload DateCreated = DateTime.UtcNow, LastModified = DateTime.UtcNow, ProjectID = noteBookData.ProjectID, - type = "new" + type = "new", + Route = user.UserName + "/" + project.Name }; await _dbContext.Notebook.AddAsync(newNotebook); await _dbContext.SaveChangesAsync(); @@ -1153,7 +1169,8 @@ public async Task UploadExistingNotebook([FromForm] ExistingProje DateCreated = DateTimeOffset.UtcNow, LastModified = DateTimeOffset.UtcNow, ProjectID = noteBookData.ProjectID, - type = "new" + type = "new", + Route = user.UserName + "/" + project.Name }; await _dbContext.Notebook.AddAsync(newNotebook); @@ -1198,6 +1215,7 @@ public async Task UploadExistingNotebook([FromForm] ExistingProje LastModified = DateTimeOffset.Now.UtcDateTime, ProjectID = noteBookData.ProjectID, type = "observable", + Route = user.UserName + "/" + project.Name, observableNotebookDatasets = observableNotebookDatasets }; @@ -1220,7 +1238,8 @@ public async Task UploadExistingNotebook([FromForm] ExistingProje DateCreated = DateTimeOffset.Now.UtcDateTime, LastModified = DateTimeOffset.Now.UtcDateTime, ProjectID = noteBookData.ProjectID, - type = "jupyter" + type = "jupyter", + Route = user.UserName + "/" + project.Name }; await _dbContext.Notebook.AddAsync(newNotebook); @@ -1367,7 +1386,8 @@ public async Task CreateNotebookFolder([FromForm] FolderUploadPro DateCreated = DateTimeOffset.UtcNow, LastModified = DateTimeOffset.UtcNow, ProjectID = formdata.ProjectID, - type = "folder" + type = "folder", + Route = user.UserName + "/" + project.Name }; // Update Database with entry @@ -2090,6 +2110,21 @@ public IActionResult GetFileList([FromRoute] int projectID) }); } + [HttpGet("[action]")] + public IActionResult GetAllDatasets() + { + var files = _dbContext.BlobFiles + .Where(b => b.Extension == ".csv") + .ToList(); + + // Return Ok Status + return Ok(new + { + result = files, + message = "Datasets Received" + }); + } + [HttpGet("[action]/{projectID}/{directory}")] public IActionResult GetNotebooks([FromRoute] int projectID, [FromRoute] string directory) {