Spaces:
Sleeping
Sleeping
Yuyuqt commited on
Commit ·
c596926
1
Parent(s): 907ba54
feat: authorization
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- Database/Database.csproj +22 -0
- Database/Models/Book.cs +31 -0
- Database/Models/Borrowing.cs +29 -0
- Database/Models/Category.cs +13 -0
- Database/Models/LibraryManagementContext.cs +175 -0
- Database/Models/Membership.cs +21 -0
- Database/Models/User.cs +39 -0
- Database/Models/UserSubscription.cs +23 -0
- Database/Models/Wishlist.cs +17 -0
- LibraryManagement.Backend/Features/Auth/AuthController.cs +44 -0
- LibraryManagement.Backend/Features/Auth/AuthModels.cs +26 -0
- LibraryManagement.Backend/Features/Auth/AuthService.cs +101 -0
- LibraryManagement.Backend/Features/Users/UserController.cs +23 -0
- LibraryManagement.Backend/Features/Users/UserService.cs +30 -0
- LibraryManagement.Backend/LibraryManagement.Backend.csproj +20 -0
- LibraryManagement.Backend/LibraryManagement.Backend.http +6 -0
- LibraryManagement.Backend/Program.cs +83 -0
- {LibraryManagement → LibraryManagement.Backend}/Properties/launchSettings.json +7 -4
- LibraryManagement.Backend/WeatherForecast.cs +13 -0
- {LibraryManagement → LibraryManagement.Backend}/appsettings.Development.json +0 -0
- LibraryManagement.Backend/appsettings.json +17 -0
- LibraryManagement.Frontend/Components/App.razor +20 -0
- LibraryManagement.Frontend/Components/Layout/MainLayout.razor +23 -0
- LibraryManagement.Frontend/Components/Layout/MainLayout.razor.css +96 -0
- LibraryManagement.Frontend/Components/Layout/NavMenu.razor +30 -0
- LibraryManagement.Frontend/Components/Layout/NavMenu.razor.css +105 -0
- LibraryManagement.Frontend/Components/Pages/Counter.razor +19 -0
- LibraryManagement/Views/Shared/Error.cshtml → LibraryManagement.Frontend/Components/Pages/Error.razor +17 -6
- LibraryManagement.Frontend/Components/Pages/Home.razor +7 -0
- LibraryManagement.Frontend/Components/Pages/Weather.razor +64 -0
- LibraryManagement.Frontend/Components/Routes.razor +6 -0
- LibraryManagement.Frontend/Components/_Imports.razor +10 -0
- LibraryManagement/LibraryManagement.csproj → LibraryManagement.Frontend/LibraryManagement.Frontend.csproj +0 -0
- {LibraryManagement → LibraryManagement.Frontend}/Program.cs +9 -9
- LibraryManagement.Frontend/Properties/launchSettings.json +38 -0
- LibraryManagement.Frontend/appsettings.Development.json +8 -0
- {LibraryManagement → LibraryManagement.Frontend}/appsettings.json +0 -0
- LibraryManagement.Frontend/wwwroot/app.css +51 -0
- {LibraryManagement/wwwroot/lib/bootstrap/dist/css → LibraryManagement.Frontend/wwwroot/bootstrap}/bootstrap.min.css +0 -0
- {LibraryManagement/wwwroot/lib/bootstrap/dist/css → LibraryManagement.Frontend/wwwroot/bootstrap}/bootstrap.min.css.map +0 -0
- LibraryManagement.Frontend/wwwroot/favicon.png +0 -0
- LibraryManagement.slnx +3 -1
- LibraryManagement/Controllers/HomeController.cs +0 -32
- LibraryManagement/Models/ErrorViewModel.cs +0 -9
- LibraryManagement/Views/Home/Index.cshtml +0 -8
- LibraryManagement/Views/Home/Privacy.cshtml +0 -6
- LibraryManagement/Views/Shared/_Layout.cshtml +0 -49
- LibraryManagement/Views/Shared/_Layout.cshtml.css +0 -48
- LibraryManagement/Views/Shared/_ValidationScriptsPartial.cshtml +0 -2
- LibraryManagement/Views/_ViewImports.cshtml +0 -3
Database/Database.csproj
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Microsoft.NET.Sdk">
|
| 2 |
+
|
| 3 |
+
<PropertyGroup>
|
| 4 |
+
<TargetFramework>net8.0</TargetFramework>
|
| 5 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 6 |
+
<Nullable>enable</Nullable>
|
| 7 |
+
</PropertyGroup>
|
| 8 |
+
|
| 9 |
+
<ItemGroup>
|
| 10 |
+
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.0" />
|
| 11 |
+
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0">
|
| 12 |
+
<PrivateAssets>all</PrivateAssets>
|
| 13 |
+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
| 14 |
+
</PackageReference>
|
| 15 |
+
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.0" />
|
| 16 |
+
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0">
|
| 17 |
+
<PrivateAssets>all</PrivateAssets>
|
| 18 |
+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
| 19 |
+
</PackageReference>
|
| 20 |
+
</ItemGroup>
|
| 21 |
+
|
| 22 |
+
</Project>
|
Database/Models/Book.cs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
|
| 4 |
+
namespace Database.Models;
|
| 5 |
+
|
| 6 |
+
public partial class Book
|
| 7 |
+
{
|
| 8 |
+
public int Id { get; set; }
|
| 9 |
+
|
| 10 |
+
public string Title { get; set; } = null!;
|
| 11 |
+
|
| 12 |
+
public string Isbn { get; set; } = null!;
|
| 13 |
+
|
| 14 |
+
public string Author { get; set; } = null!;
|
| 15 |
+
|
| 16 |
+
public string Status { get; set; } = null!;
|
| 17 |
+
|
| 18 |
+
public bool IsActive { get; set; }
|
| 19 |
+
|
| 20 |
+
public DateTime CreatedAt { get; set; }
|
| 21 |
+
|
| 22 |
+
public string? Description { get; set; }
|
| 23 |
+
|
| 24 |
+
public string? CoverUrl { get; set; }
|
| 25 |
+
|
| 26 |
+
public virtual ICollection<Borrowing> Borrowings { get; set; } = new List<Borrowing>();
|
| 27 |
+
|
| 28 |
+
public virtual ICollection<Wishlist> Wishlists { get; set; } = new List<Wishlist>();
|
| 29 |
+
|
| 30 |
+
public virtual ICollection<Category> Categories { get; set; } = new List<Category>();
|
| 31 |
+
}
|
Database/Models/Borrowing.cs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
|
| 4 |
+
namespace Database.Models;
|
| 5 |
+
|
| 6 |
+
public partial class Borrowing
|
| 7 |
+
{
|
| 8 |
+
public int Id { get; set; }
|
| 9 |
+
|
| 10 |
+
public int UserId { get; set; }
|
| 11 |
+
|
| 12 |
+
public int BookId { get; set; }
|
| 13 |
+
|
| 14 |
+
public DateTime BorrowDate { get; set; }
|
| 15 |
+
|
| 16 |
+
public DateTime DueDate { get; set; }
|
| 17 |
+
|
| 18 |
+
public DateTime? ReturnDate { get; set; }
|
| 19 |
+
|
| 20 |
+
public string Status { get; set; } = null!;
|
| 21 |
+
|
| 22 |
+
public decimal FineAmount { get; set; }
|
| 23 |
+
|
| 24 |
+
public bool IsFinePaid { get; set; }
|
| 25 |
+
|
| 26 |
+
public virtual Book Book { get; set; } = null!;
|
| 27 |
+
|
| 28 |
+
public virtual User User { get; set; } = null!;
|
| 29 |
+
}
|
Database/Models/Category.cs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
|
| 4 |
+
namespace Database.Models;
|
| 5 |
+
|
| 6 |
+
public partial class Category
|
| 7 |
+
{
|
| 8 |
+
public int Id { get; set; }
|
| 9 |
+
|
| 10 |
+
public string Name { get; set; } = null!;
|
| 11 |
+
|
| 12 |
+
public virtual ICollection<Book> Books { get; set; } = new List<Book>();
|
| 13 |
+
}
|
Database/Models/LibraryManagementContext.cs
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
using Microsoft.EntityFrameworkCore;
|
| 4 |
+
|
| 5 |
+
namespace Database.Models;
|
| 6 |
+
|
| 7 |
+
public partial class LibraryManagementContext : DbContext
|
| 8 |
+
{
|
| 9 |
+
public LibraryManagementContext()
|
| 10 |
+
{
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
public LibraryManagementContext(DbContextOptions<LibraryManagementContext> options)
|
| 14 |
+
: base(options)
|
| 15 |
+
{
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
public virtual DbSet<Book> Books { get; set; }
|
| 19 |
+
|
| 20 |
+
public virtual DbSet<Borrowing> Borrowings { get; set; }
|
| 21 |
+
|
| 22 |
+
public virtual DbSet<Category> Categories { get; set; }
|
| 23 |
+
|
| 24 |
+
public virtual DbSet<Membership> Memberships { get; set; }
|
| 25 |
+
|
| 26 |
+
public virtual DbSet<User> Users { get; set; }
|
| 27 |
+
|
| 28 |
+
public virtual DbSet<UserSubscription> UserSubscriptions { get; set; }
|
| 29 |
+
|
| 30 |
+
public virtual DbSet<Wishlist> Wishlists { get; set; }
|
| 31 |
+
|
| 32 |
+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
| 33 |
+
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see https://go.microsoft.com/fwlink/?LinkId=723263.
|
| 34 |
+
=> optionsBuilder.UseSqlServer("Server=DESKTOP-BP9A061;Database=LibraryManagement;User Id=sa;Password=sasa@123;TrustServerCertificate=True;");
|
| 35 |
+
|
| 36 |
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
| 37 |
+
{
|
| 38 |
+
modelBuilder.Entity<Book>(entity =>
|
| 39 |
+
{
|
| 40 |
+
entity.HasKey(e => e.Id).HasName("PK__Books__3214EC07AE1974C8");
|
| 41 |
+
|
| 42 |
+
entity.HasIndex(e => e.Isbn, "UQ__Books__447D36EA79CF66CE").IsUnique();
|
| 43 |
+
|
| 44 |
+
entity.Property(e => e.Author).HasMaxLength(100);
|
| 45 |
+
entity.Property(e => e.CreatedAt)
|
| 46 |
+
.HasDefaultValueSql("(getdate())")
|
| 47 |
+
.HasColumnType("datetime");
|
| 48 |
+
entity.Property(e => e.IsActive).HasDefaultValue(true);
|
| 49 |
+
entity.Property(e => e.Isbn)
|
| 50 |
+
.HasMaxLength(20)
|
| 51 |
+
.HasColumnName("ISBN");
|
| 52 |
+
entity.Property(e => e.Status)
|
| 53 |
+
.HasMaxLength(20)
|
| 54 |
+
.HasDefaultValue("Available");
|
| 55 |
+
entity.Property(e => e.Title).HasMaxLength(200);
|
| 56 |
+
|
| 57 |
+
entity.HasMany(d => d.Categories).WithMany(p => p.Books)
|
| 58 |
+
.UsingEntity<Dictionary<string, object>>(
|
| 59 |
+
"BookCategory",
|
| 60 |
+
r => r.HasOne<Category>().WithMany()
|
| 61 |
+
.HasForeignKey("CategoryId")
|
| 62 |
+
.HasConstraintName("FK_BookCategories_Categories"),
|
| 63 |
+
l => l.HasOne<Book>().WithMany()
|
| 64 |
+
.HasForeignKey("BookId")
|
| 65 |
+
.HasConstraintName("FK_BookCategories_Books"),
|
| 66 |
+
j =>
|
| 67 |
+
{
|
| 68 |
+
j.HasKey("BookId", "CategoryId").HasName("PK__BookCate__9C7051A74E7D221D");
|
| 69 |
+
j.ToTable("BookCategories");
|
| 70 |
+
});
|
| 71 |
+
});
|
| 72 |
+
|
| 73 |
+
modelBuilder.Entity<Borrowing>(entity =>
|
| 74 |
+
{
|
| 75 |
+
entity.HasKey(e => e.Id).HasName("PK__Borrowin__3214EC07EA26D8CD");
|
| 76 |
+
|
| 77 |
+
entity.Property(e => e.BorrowDate)
|
| 78 |
+
.HasDefaultValueSql("(getdate())")
|
| 79 |
+
.HasColumnType("datetime");
|
| 80 |
+
entity.Property(e => e.DueDate).HasColumnType("datetime");
|
| 81 |
+
entity.Property(e => e.FineAmount).HasColumnType("decimal(10, 2)");
|
| 82 |
+
entity.Property(e => e.ReturnDate).HasColumnType("datetime");
|
| 83 |
+
entity.Property(e => e.Status)
|
| 84 |
+
.HasMaxLength(20)
|
| 85 |
+
.HasDefaultValue("Active");
|
| 86 |
+
|
| 87 |
+
entity.HasOne(d => d.Book).WithMany(p => p.Borrowings)
|
| 88 |
+
.HasForeignKey(d => d.BookId)
|
| 89 |
+
.OnDelete(DeleteBehavior.ClientSetNull)
|
| 90 |
+
.HasConstraintName("FK_Borrowings_Books");
|
| 91 |
+
|
| 92 |
+
entity.HasOne(d => d.User).WithMany(p => p.Borrowings)
|
| 93 |
+
.HasForeignKey(d => d.UserId)
|
| 94 |
+
.OnDelete(DeleteBehavior.ClientSetNull)
|
| 95 |
+
.HasConstraintName("FK_Borrowings_Users");
|
| 96 |
+
});
|
| 97 |
+
|
| 98 |
+
modelBuilder.Entity<Category>(entity =>
|
| 99 |
+
{
|
| 100 |
+
entity.HasKey(e => e.Id).HasName("PK__Categori__3214EC0751B5797D");
|
| 101 |
+
|
| 102 |
+
entity.HasIndex(e => e.Name, "UQ__Categori__737584F622B16D3A").IsUnique();
|
| 103 |
+
|
| 104 |
+
entity.Property(e => e.Name).HasMaxLength(100);
|
| 105 |
+
});
|
| 106 |
+
|
| 107 |
+
modelBuilder.Entity<Membership>(entity =>
|
| 108 |
+
{
|
| 109 |
+
entity.HasKey(e => e.Id).HasName("PK__Membersh__3214EC07A6F5A55F");
|
| 110 |
+
|
| 111 |
+
entity.HasIndex(e => e.Type, "IX_Memberships_Type").IsUnique();
|
| 112 |
+
|
| 113 |
+
entity.Property(e => e.Price).HasColumnType("decimal(10, 2)");
|
| 114 |
+
entity.Property(e => e.Type).HasMaxLength(50);
|
| 115 |
+
});
|
| 116 |
+
|
| 117 |
+
modelBuilder.Entity<User>(entity =>
|
| 118 |
+
{
|
| 119 |
+
entity.HasKey(e => e.Id).HasName("PK__Users__3214EC0771D7BED2");
|
| 120 |
+
|
| 121 |
+
entity.HasIndex(e => e.Email, "UQ__Users__A9D10534F91FFF73").IsUnique();
|
| 122 |
+
|
| 123 |
+
entity.Property(e => e.Address).HasMaxLength(255);
|
| 124 |
+
entity.Property(e => e.CreatedAt)
|
| 125 |
+
.HasDefaultValueSql("(getdate())")
|
| 126 |
+
.HasColumnType("datetime");
|
| 127 |
+
entity.Property(e => e.Email).HasMaxLength(100);
|
| 128 |
+
entity.Property(e => e.FullName).HasMaxLength(100);
|
| 129 |
+
entity.Property(e => e.IsActive).HasDefaultValue(true);
|
| 130 |
+
entity.Property(e => e.PhoneNumber).HasMaxLength(20);
|
| 131 |
+
entity.Property(e => e.Role)
|
| 132 |
+
.HasMaxLength(20)
|
| 133 |
+
.HasDefaultValue("User");
|
| 134 |
+
entity.Property(e => e.StudentId).HasMaxLength(20);
|
| 135 |
+
entity.Property(e => e.SuspensionEndDate).HasColumnType("datetime");
|
| 136 |
+
entity.Property(e => e.UpdatedAt).HasColumnType("datetime");
|
| 137 |
+
});
|
| 138 |
+
|
| 139 |
+
modelBuilder.Entity<UserSubscription>(entity =>
|
| 140 |
+
{
|
| 141 |
+
entity.HasKey(e => e.Id).HasName("PK__UserSubs__3214EC07E3FBC0EC");
|
| 142 |
+
|
| 143 |
+
entity.Property(e => e.ExpiryDate).HasColumnType("datetime");
|
| 144 |
+
entity.Property(e => e.IsActive).HasDefaultValue(true);
|
| 145 |
+
entity.Property(e => e.StartDate).HasColumnType("datetime");
|
| 146 |
+
|
| 147 |
+
entity.HasOne(d => d.Membership).WithMany(p => p.UserSubscriptions)
|
| 148 |
+
.HasForeignKey(d => d.MembershipId)
|
| 149 |
+
.OnDelete(DeleteBehavior.ClientSetNull)
|
| 150 |
+
.HasConstraintName("FK_UserSubscriptions_Memberships");
|
| 151 |
+
|
| 152 |
+
entity.HasOne(d => d.User).WithMany(p => p.UserSubscriptions)
|
| 153 |
+
.HasForeignKey(d => d.UserId)
|
| 154 |
+
.OnDelete(DeleteBehavior.ClientSetNull)
|
| 155 |
+
.HasConstraintName("FK_UserSubscriptions_Users");
|
| 156 |
+
});
|
| 157 |
+
|
| 158 |
+
modelBuilder.Entity<Wishlist>(entity =>
|
| 159 |
+
{
|
| 160 |
+
entity.HasKey(e => e.Id).HasName("PK__Wishlist__3214EC07F63D8F81");
|
| 161 |
+
|
| 162 |
+
entity.HasOne(d => d.Book).WithMany(p => p.Wishlists)
|
| 163 |
+
.HasForeignKey(d => d.BookId)
|
| 164 |
+
.HasConstraintName("FK_Wishlists_Books");
|
| 165 |
+
|
| 166 |
+
entity.HasOne(d => d.User).WithMany(p => p.Wishlists)
|
| 167 |
+
.HasForeignKey(d => d.UserId)
|
| 168 |
+
.HasConstraintName("FK_Wishlists_Users");
|
| 169 |
+
});
|
| 170 |
+
|
| 171 |
+
OnModelCreatingPartial(modelBuilder);
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
|
| 175 |
+
}
|
Database/Models/Membership.cs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
|
| 4 |
+
namespace Database.Models;
|
| 5 |
+
|
| 6 |
+
public partial class Membership
|
| 7 |
+
{
|
| 8 |
+
public int Id { get; set; }
|
| 9 |
+
|
| 10 |
+
public string Type { get; set; } = null!;
|
| 11 |
+
|
| 12 |
+
public int MaxBooks { get; set; }
|
| 13 |
+
|
| 14 |
+
public int BorrowingDays { get; set; }
|
| 15 |
+
|
| 16 |
+
public decimal Price { get; set; }
|
| 17 |
+
|
| 18 |
+
public int DurationMonths { get; set; }
|
| 19 |
+
|
| 20 |
+
public virtual ICollection<UserSubscription> UserSubscriptions { get; set; } = new List<UserSubscription>();
|
| 21 |
+
}
|
Database/Models/User.cs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
|
| 4 |
+
namespace Database.Models;
|
| 5 |
+
|
| 6 |
+
public partial class User
|
| 7 |
+
{
|
| 8 |
+
public int Id { get; set; }
|
| 9 |
+
|
| 10 |
+
public string FullName { get; set; } = null!;
|
| 11 |
+
|
| 12 |
+
public string Email { get; set; } = null!;
|
| 13 |
+
|
| 14 |
+
public string PasswordHash { get; set; } = null!;
|
| 15 |
+
|
| 16 |
+
public string? PhoneNumber { get; set; }
|
| 17 |
+
|
| 18 |
+
public string Role { get; set; } = null!;
|
| 19 |
+
|
| 20 |
+
public bool IsActive { get; set; }
|
| 21 |
+
|
| 22 |
+
public DateTime CreatedAt { get; set; }
|
| 23 |
+
|
| 24 |
+
public DateTime? UpdatedAt { get; set; }
|
| 25 |
+
|
| 26 |
+
public string? StudentId { get; set; }
|
| 27 |
+
|
| 28 |
+
public bool? BanStatus { get; set; }
|
| 29 |
+
|
| 30 |
+
public DateTime? SuspensionEndDate { get; set; }
|
| 31 |
+
|
| 32 |
+
public string? Address { get; set; }
|
| 33 |
+
|
| 34 |
+
public virtual ICollection<Borrowing> Borrowings { get; set; } = new List<Borrowing>();
|
| 35 |
+
|
| 36 |
+
public virtual ICollection<UserSubscription> UserSubscriptions { get; set; } = new List<UserSubscription>();
|
| 37 |
+
|
| 38 |
+
public virtual ICollection<Wishlist> Wishlists { get; set; } = new List<Wishlist>();
|
| 39 |
+
}
|
Database/Models/UserSubscription.cs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
|
| 4 |
+
namespace Database.Models;
|
| 5 |
+
|
| 6 |
+
public partial class UserSubscription
|
| 7 |
+
{
|
| 8 |
+
public int Id { get; set; }
|
| 9 |
+
|
| 10 |
+
public int UserId { get; set; }
|
| 11 |
+
|
| 12 |
+
public int MembershipId { get; set; }
|
| 13 |
+
|
| 14 |
+
public DateTime StartDate { get; set; }
|
| 15 |
+
|
| 16 |
+
public DateTime ExpiryDate { get; set; }
|
| 17 |
+
|
| 18 |
+
public bool IsActive { get; set; }
|
| 19 |
+
|
| 20 |
+
public virtual Membership Membership { get; set; } = null!;
|
| 21 |
+
|
| 22 |
+
public virtual User User { get; set; } = null!;
|
| 23 |
+
}
|
Database/Models/Wishlist.cs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System;
|
| 2 |
+
using System.Collections.Generic;
|
| 3 |
+
|
| 4 |
+
namespace Database.Models;
|
| 5 |
+
|
| 6 |
+
public partial class Wishlist
|
| 7 |
+
{
|
| 8 |
+
public int Id { get; set; }
|
| 9 |
+
|
| 10 |
+
public int UserId { get; set; }
|
| 11 |
+
|
| 12 |
+
public int BookId { get; set; }
|
| 13 |
+
|
| 14 |
+
public virtual Book Book { get; set; } = null!;
|
| 15 |
+
|
| 16 |
+
public virtual User User { get; set; } = null!;
|
| 17 |
+
}
|
LibraryManagement.Backend/Features/Auth/AuthController.cs
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using Microsoft.AspNetCore.Mvc;
|
| 2 |
+
|
| 3 |
+
namespace LibraryManagement.Backend.Features.Auth
|
| 4 |
+
{
|
| 5 |
+
[ApiController]
|
| 6 |
+
[Route("api/auth")]
|
| 7 |
+
public class AuthController : ControllerBase
|
| 8 |
+
{
|
| 9 |
+
private readonly IAuthService _authService;
|
| 10 |
+
|
| 11 |
+
public AuthController(IAuthService authService)
|
| 12 |
+
{
|
| 13 |
+
_authService = authService;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
[HttpPost("register")]
|
| 17 |
+
public async Task<ActionResult<AuthResponse>> Register([FromBody] RegisterRequest request)
|
| 18 |
+
{
|
| 19 |
+
try
|
| 20 |
+
{
|
| 21 |
+
var response = await _authService.Register(request);
|
| 22 |
+
return Ok(response);
|
| 23 |
+
}
|
| 24 |
+
catch (Exception ex)
|
| 25 |
+
{
|
| 26 |
+
return BadRequest(new { message = ex.Message });
|
| 27 |
+
}
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
[HttpPost("login")]
|
| 31 |
+
public async Task<ActionResult<AuthResponse>> Login([FromBody] LoginRequest request)
|
| 32 |
+
{
|
| 33 |
+
try
|
| 34 |
+
{
|
| 35 |
+
var response = await _authService.Login(request);
|
| 36 |
+
return Ok(response);
|
| 37 |
+
}
|
| 38 |
+
catch (Exception ex)
|
| 39 |
+
{
|
| 40 |
+
return Unauthorized(new { message = ex.Message });
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
}
|
LibraryManagement.Backend/Features/Auth/AuthModels.cs
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
namespace LibraryManagement.Backend.Features.Auth
|
| 2 |
+
{
|
| 3 |
+
public class RegisterRequest
|
| 4 |
+
{
|
| 5 |
+
public string FullName { get; set; } = string.Empty;
|
| 6 |
+
public string Email { get; set; } = string.Empty;
|
| 7 |
+
public string Password { get; set; } = string.Empty;
|
| 8 |
+
public string? PhoneNumber { get; set; }
|
| 9 |
+
public string? Address { get; set; }
|
| 10 |
+
public string? StudentId { get; set; }
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
public class LoginRequest
|
| 14 |
+
{
|
| 15 |
+
public string Email { get; set; } = string.Empty;
|
| 16 |
+
public string Password { get; set; } = string.Empty;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
public class AuthResponse
|
| 20 |
+
{
|
| 21 |
+
public string Token { get; set; } = string.Empty;
|
| 22 |
+
public string FullName { get; set; } = string.Empty;
|
| 23 |
+
public string Role { get; set; } = string.Empty;
|
| 24 |
+
public DateTime Expiry { get; set; }
|
| 25 |
+
}
|
| 26 |
+
}
|
LibraryManagement.Backend/Features/Auth/AuthService.cs
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using Database.Models;
|
| 2 |
+
using Microsoft.EntityFrameworkCore;
|
| 3 |
+
using Microsoft.IdentityModel.Tokens;
|
| 4 |
+
using System.IdentityModel.Tokens.Jwt;
|
| 5 |
+
using System.Security.Claims;
|
| 6 |
+
using System.Text;
|
| 7 |
+
|
| 8 |
+
namespace LibraryManagement.Backend.Features.Auth
|
| 9 |
+
{
|
| 10 |
+
public interface IAuthService
|
| 11 |
+
{
|
| 12 |
+
Task<AuthResponse> Register(RegisterRequest request);
|
| 13 |
+
Task<AuthResponse> Login(LoginRequest request);
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
public class AuthService : IAuthService
|
| 17 |
+
{
|
| 18 |
+
private readonly LibraryManagementContext _context;
|
| 19 |
+
private readonly IConfiguration _configuration;
|
| 20 |
+
|
| 21 |
+
public AuthService(LibraryManagementContext context, IConfiguration configuration)
|
| 22 |
+
{
|
| 23 |
+
_context = context;
|
| 24 |
+
_configuration = configuration;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
public async Task<AuthResponse> Register(RegisterRequest request)
|
| 28 |
+
{
|
| 29 |
+
if (await _context.Users.AnyAsync(u => u.Email == request.Email))
|
| 30 |
+
{
|
| 31 |
+
throw new Exception("User already exists with this email.");
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
var user = new User
|
| 35 |
+
{
|
| 36 |
+
FullName = request.FullName,
|
| 37 |
+
Email = request.Email,
|
| 38 |
+
PhoneNumber = request.PhoneNumber,
|
| 39 |
+
Address = request.Address,
|
| 40 |
+
StudentId = request.StudentId,
|
| 41 |
+
PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password),
|
| 42 |
+
Role = "Member", // Default role
|
| 43 |
+
IsActive = true,
|
| 44 |
+
CreatedAt = DateTime.UtcNow
|
| 45 |
+
};
|
| 46 |
+
|
| 47 |
+
_context.Users.Add(user);
|
| 48 |
+
await _context.SaveChangesAsync();
|
| 49 |
+
|
| 50 |
+
return await AuthenticateUser(user);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
public async Task<AuthResponse> Login(LoginRequest request)
|
| 54 |
+
{
|
| 55 |
+
var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == request.Email);
|
| 56 |
+
|
| 57 |
+
if (user == null || !BCrypt.Net.BCrypt.Verify(request.Password, user.PasswordHash))
|
| 58 |
+
{
|
| 59 |
+
throw new Exception("Invalid email or password.");
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
if (!user.IsActive)
|
| 63 |
+
{
|
| 64 |
+
throw new Exception("User account is deactivated.");
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
return await AuthenticateUser(user);
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
private async Task<AuthResponse> AuthenticateUser(User user)
|
| 71 |
+
{
|
| 72 |
+
var tokenHandler = new JwtSecurityTokenHandler();
|
| 73 |
+
var key = Encoding.ASCII.GetBytes(_configuration["Jwt:Secret"] ?? "YourSuperSecretKeyForLibraryManagementSystem_AtLeast32CharsLong");
|
| 74 |
+
|
| 75 |
+
var tokenDescriptor = new SecurityTokenDescriptor
|
| 76 |
+
{
|
| 77 |
+
Subject = new ClaimsIdentity(new[]
|
| 78 |
+
{
|
| 79 |
+
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
| 80 |
+
new Claim(ClaimTypes.Email, user.Email),
|
| 81 |
+
new Claim(ClaimTypes.Name, user.FullName),
|
| 82 |
+
new Claim(ClaimTypes.Role, user.Role)
|
| 83 |
+
}),
|
| 84 |
+
Expires = DateTime.UtcNow.AddDays(7),
|
| 85 |
+
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
|
| 86 |
+
Issuer = _configuration["Jwt:Issuer"],
|
| 87 |
+
Audience = _configuration["Jwt:Audience"]
|
| 88 |
+
};
|
| 89 |
+
|
| 90 |
+
var token = tokenHandler.CreateToken(tokenDescriptor);
|
| 91 |
+
|
| 92 |
+
return new AuthResponse
|
| 93 |
+
{
|
| 94 |
+
Token = tokenHandler.WriteToken(token),
|
| 95 |
+
FullName = user.FullName,
|
| 96 |
+
Role = user.Role,
|
| 97 |
+
Expiry = tokenDescriptor.Expires.Value
|
| 98 |
+
};
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
}
|
LibraryManagement.Backend/Features/Users/UserController.cs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using Microsoft.AspNetCore.Mvc;
|
| 2 |
+
|
| 3 |
+
namespace LibraryManagement.Backend.Features.Users
|
| 4 |
+
{
|
| 5 |
+
[ApiController]
|
| 6 |
+
[Route("api/users")]
|
| 7 |
+
public class UserController : ControllerBase
|
| 8 |
+
{
|
| 9 |
+
private readonly IUserService _userService;
|
| 10 |
+
|
| 11 |
+
public UserController(IUserService userService)
|
| 12 |
+
{
|
| 13 |
+
_userService = userService;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
[HttpPost]
|
| 17 |
+
public async Task<ActionResult<UserCreateResponse>> CreateUser([FromBody] UserCreateRequest request)
|
| 18 |
+
{
|
| 19 |
+
var response = await _userService.CreateUser(request);
|
| 20 |
+
return Ok(response);
|
| 21 |
+
}
|
| 22 |
+
}
|
| 23 |
+
}
|
LibraryManagement.Backend/Features/Users/UserService.cs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using Database.Models;
|
| 2 |
+
|
| 3 |
+
namespace LibraryManagement.Backend.Features.Users
|
| 4 |
+
{
|
| 5 |
+
public interface IUserService
|
| 6 |
+
{
|
| 7 |
+
public Task<UserCreateResponse> CreateUser(UserCreateRequest request);
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
public class UserService : IUserService
|
| 11 |
+
{
|
| 12 |
+
private readonly LibraryManagementContext _context;
|
| 13 |
+
|
| 14 |
+
public UserService(LibraryManagementContext context) {
|
| 15 |
+
_context = context;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
public Task<UserCreateResponse> CreateUser(UserCreateRequest request)
|
| 19 |
+
{
|
| 20 |
+
throw new NotImplementedException();
|
| 21 |
+
}
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
public class UserCreateRequest {
|
| 25 |
+
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
public class UserCreateResponse {
|
| 29 |
+
}
|
| 30 |
+
}
|
LibraryManagement.Backend/LibraryManagement.Backend.csproj
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Project Sdk="Microsoft.NET.Sdk.Web">
|
| 2 |
+
|
| 3 |
+
<PropertyGroup>
|
| 4 |
+
<TargetFramework>net8.0</TargetFramework>
|
| 5 |
+
<Nullable>enable</Nullable>
|
| 6 |
+
<ImplicitUsings>enable</ImplicitUsings>
|
| 7 |
+
</PropertyGroup>
|
| 8 |
+
|
| 9 |
+
<ItemGroup>
|
| 10 |
+
<PackageReference Include="BCrypt.Net-Next" Version="4.1.0" />
|
| 11 |
+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" />
|
| 12 |
+
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
| 13 |
+
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.17.0" />
|
| 14 |
+
</ItemGroup>
|
| 15 |
+
|
| 16 |
+
<ItemGroup>
|
| 17 |
+
<ProjectReference Include="..\Database\Database.csproj" />
|
| 18 |
+
</ItemGroup>
|
| 19 |
+
|
| 20 |
+
</Project>
|
LibraryManagement.Backend/LibraryManagement.Backend.http
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@LibraryManagement.Backend_HostAddress = http://localhost:5199
|
| 2 |
+
|
| 3 |
+
GET {{LibraryManagement.Backend_HostAddress}}/weatherforecast/
|
| 4 |
+
Accept: application/json
|
| 5 |
+
|
| 6 |
+
###
|
LibraryManagement.Backend/Program.cs
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
using System.Text;
|
| 2 |
+
using Database.Models;
|
| 3 |
+
using LibraryManagement.Backend.Features.Auth;
|
| 4 |
+
using LibraryManagement.Backend.Features.Users;
|
| 5 |
+
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
| 6 |
+
using Microsoft.EntityFrameworkCore;
|
| 7 |
+
using Microsoft.IdentityModel.Tokens;
|
| 8 |
+
using Microsoft.OpenApi.Models;
|
| 9 |
+
|
| 10 |
+
var builder = WebApplication.CreateBuilder(args);
|
| 11 |
+
|
| 12 |
+
builder.Services.AddControllers();
|
| 13 |
+
builder.Services.AddEndpointsApiExplorer();
|
| 14 |
+
builder.Services.AddSwaggerGen(c =>
|
| 15 |
+
{
|
| 16 |
+
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Library Management API", Version = "v1" });
|
| 17 |
+
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
| 18 |
+
{
|
| 19 |
+
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
|
| 20 |
+
Name = "Authorization",
|
| 21 |
+
In = ParameterLocation.Header,
|
| 22 |
+
Type = SecuritySchemeType.ApiKey,
|
| 23 |
+
Scheme = "Bearer"
|
| 24 |
+
});
|
| 25 |
+
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
| 26 |
+
{
|
| 27 |
+
{
|
| 28 |
+
new OpenApiSecurityScheme
|
| 29 |
+
{
|
| 30 |
+
Reference = new OpenApiReference
|
| 31 |
+
{
|
| 32 |
+
Type = ReferenceType.SecurityScheme,
|
| 33 |
+
Id = "Bearer"
|
| 34 |
+
}
|
| 35 |
+
},
|
| 36 |
+
new string[] {}
|
| 37 |
+
}
|
| 38 |
+
});
|
| 39 |
+
});
|
| 40 |
+
|
| 41 |
+
builder.Services.AddDbContext<LibraryManagementContext>(options =>
|
| 42 |
+
options.UseSqlServer(builder.Configuration.GetConnectionString("MssqlConnection")));
|
| 43 |
+
|
| 44 |
+
// Register Services
|
| 45 |
+
builder.Services.AddScoped<IAuthService, AuthService>();
|
| 46 |
+
builder.Services.AddScoped<IUserService, UserService>();
|
| 47 |
+
|
| 48 |
+
// Configure JWT Authentication
|
| 49 |
+
var key = Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Secret"] ?? "YourSuperSecretKeyForLibraryManagementSystem_AtLeast32CharsLong");
|
| 50 |
+
builder.Services.AddAuthentication(x =>
|
| 51 |
+
{
|
| 52 |
+
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
| 53 |
+
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
| 54 |
+
})
|
| 55 |
+
.AddJwtBearer(x =>
|
| 56 |
+
{
|
| 57 |
+
x.RequireHttpsMetadata = false;
|
| 58 |
+
x.SaveToken = true;
|
| 59 |
+
x.TokenValidationParameters = new TokenValidationParameters
|
| 60 |
+
{
|
| 61 |
+
ValidateIssuerSigningKey = true,
|
| 62 |
+
IssuerSigningKey = new SymmetricSecurityKey(key),
|
| 63 |
+
ValidateIssuer = true,
|
| 64 |
+
ValidateAudience = true,
|
| 65 |
+
ValidIssuer = builder.Configuration["Jwt:Issuer"],
|
| 66 |
+
ValidAudience = builder.Configuration["Jwt:Audience"],
|
| 67 |
+
ClockSkew = TimeSpan.Zero
|
| 68 |
+
};
|
| 69 |
+
});
|
| 70 |
+
|
| 71 |
+
var app = builder.Build();
|
| 72 |
+
|
| 73 |
+
app.UseSwagger();
|
| 74 |
+
app.UseSwaggerUI();
|
| 75 |
+
|
| 76 |
+
app.UseHttpsRedirection();
|
| 77 |
+
|
| 78 |
+
app.UseAuthentication();
|
| 79 |
+
app.UseAuthorization();
|
| 80 |
+
|
| 81 |
+
app.MapControllers();
|
| 82 |
+
|
| 83 |
+
app.Run();
|
{LibraryManagement → LibraryManagement.Backend}/Properties/launchSettings.json
RENAMED
|
@@ -4,8 +4,8 @@
|
|
| 4 |
"windowsAuthentication": false,
|
| 5 |
"anonymousAuthentication": true,
|
| 6 |
"iisExpress": {
|
| 7 |
-
"applicationUrl": "http://localhost:
|
| 8 |
-
"sslPort":
|
| 9 |
}
|
| 10 |
},
|
| 11 |
"profiles": {
|
|
@@ -13,7 +13,8 @@
|
|
| 13 |
"commandName": "Project",
|
| 14 |
"dotnetRunMessages": true,
|
| 15 |
"launchBrowser": true,
|
| 16 |
-
"
|
|
|
|
| 17 |
"environmentVariables": {
|
| 18 |
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 19 |
}
|
|
@@ -22,7 +23,8 @@
|
|
| 22 |
"commandName": "Project",
|
| 23 |
"dotnetRunMessages": true,
|
| 24 |
"launchBrowser": true,
|
| 25 |
-
"
|
|
|
|
| 26 |
"environmentVariables": {
|
| 27 |
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 28 |
}
|
|
@@ -30,6 +32,7 @@
|
|
| 30 |
"IIS Express": {
|
| 31 |
"commandName": "IISExpress",
|
| 32 |
"launchBrowser": true,
|
|
|
|
| 33 |
"environmentVariables": {
|
| 34 |
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 35 |
}
|
|
|
|
| 4 |
"windowsAuthentication": false,
|
| 5 |
"anonymousAuthentication": true,
|
| 6 |
"iisExpress": {
|
| 7 |
+
"applicationUrl": "http://localhost:13110",
|
| 8 |
+
"sslPort": 44374
|
| 9 |
}
|
| 10 |
},
|
| 11 |
"profiles": {
|
|
|
|
| 13 |
"commandName": "Project",
|
| 14 |
"dotnetRunMessages": true,
|
| 15 |
"launchBrowser": true,
|
| 16 |
+
"launchUrl": "swagger",
|
| 17 |
+
"applicationUrl": "http://localhost:5199",
|
| 18 |
"environmentVariables": {
|
| 19 |
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 20 |
}
|
|
|
|
| 23 |
"commandName": "Project",
|
| 24 |
"dotnetRunMessages": true,
|
| 25 |
"launchBrowser": true,
|
| 26 |
+
"launchUrl": "swagger",
|
| 27 |
+
"applicationUrl": "https://localhost:7028;http://localhost:5199",
|
| 28 |
"environmentVariables": {
|
| 29 |
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 30 |
}
|
|
|
|
| 32 |
"IIS Express": {
|
| 33 |
"commandName": "IISExpress",
|
| 34 |
"launchBrowser": true,
|
| 35 |
+
"launchUrl": "swagger",
|
| 36 |
"environmentVariables": {
|
| 37 |
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 38 |
}
|
LibraryManagement.Backend/WeatherForecast.cs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
namespace LibraryManagement.Backend
|
| 2 |
+
{
|
| 3 |
+
public class WeatherForecast
|
| 4 |
+
{
|
| 5 |
+
public DateOnly Date { get; set; }
|
| 6 |
+
|
| 7 |
+
public int TemperatureC { get; set; }
|
| 8 |
+
|
| 9 |
+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
| 10 |
+
|
| 11 |
+
public string? Summary { get; set; }
|
| 12 |
+
}
|
| 13 |
+
}
|
{LibraryManagement → LibraryManagement.Backend}/appsettings.Development.json
RENAMED
|
File without changes
|
LibraryManagement.Backend/appsettings.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"Logging": {
|
| 3 |
+
"LogLevel": {
|
| 4 |
+
"Default": "Information",
|
| 5 |
+
"Microsoft.AspNetCore": "Warning"
|
| 6 |
+
}
|
| 7 |
+
},
|
| 8 |
+
"AllowedHosts": "*",
|
| 9 |
+
"ConnectionStrings": {
|
| 10 |
+
"MssqlConnection": "Server=DESKTOP-BP9A061;Database=LibraryManagement;User Id=sa;Password=sasa@123;TrustServerCertificate=True;"
|
| 11 |
+
},
|
| 12 |
+
"Jwt": {
|
| 13 |
+
"Secret": "YourSuperSecretKeyForLibraryManagementSystem_AtLeast32CharsLong",
|
| 14 |
+
"Issuer": "LibraryManagementSystem",
|
| 15 |
+
"Audience": "LibraryManagementUsers"
|
| 16 |
+
}
|
| 17 |
+
}
|
LibraryManagement.Frontend/Components/App.razor
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="utf-8" />
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 7 |
+
<base href="/" />
|
| 8 |
+
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
|
| 9 |
+
<link rel="stylesheet" href="app.css" />
|
| 10 |
+
<link rel="stylesheet" href="LibraryManagement.Frontend.styles.css" />
|
| 11 |
+
<link rel="icon" type="image/png" href="favicon.png" />
|
| 12 |
+
<HeadOutlet />
|
| 13 |
+
</head>
|
| 14 |
+
|
| 15 |
+
<body>
|
| 16 |
+
<Routes />
|
| 17 |
+
<script src="_framework/blazor.web.js"></script>
|
| 18 |
+
</body>
|
| 19 |
+
|
| 20 |
+
</html>
|
LibraryManagement.Frontend/Components/Layout/MainLayout.razor
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@inherits LayoutComponentBase
|
| 2 |
+
|
| 3 |
+
<div class="page">
|
| 4 |
+
<div class="sidebar">
|
| 5 |
+
<NavMenu />
|
| 6 |
+
</div>
|
| 7 |
+
|
| 8 |
+
<main>
|
| 9 |
+
<div class="top-row px-4">
|
| 10 |
+
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
| 11 |
+
</div>
|
| 12 |
+
|
| 13 |
+
<article class="content px-4">
|
| 14 |
+
@Body
|
| 15 |
+
</article>
|
| 16 |
+
</main>
|
| 17 |
+
</div>
|
| 18 |
+
|
| 19 |
+
<div id="blazor-error-ui">
|
| 20 |
+
An unhandled error has occurred.
|
| 21 |
+
<a href="" class="reload">Reload</a>
|
| 22 |
+
<a class="dismiss">🗙</a>
|
| 23 |
+
</div>
|
LibraryManagement.Frontend/Components/Layout/MainLayout.razor.css
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.page {
|
| 2 |
+
position: relative;
|
| 3 |
+
display: flex;
|
| 4 |
+
flex-direction: column;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
main {
|
| 8 |
+
flex: 1;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
.sidebar {
|
| 12 |
+
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.top-row {
|
| 16 |
+
background-color: #f7f7f7;
|
| 17 |
+
border-bottom: 1px solid #d6d5d5;
|
| 18 |
+
justify-content: flex-end;
|
| 19 |
+
height: 3.5rem;
|
| 20 |
+
display: flex;
|
| 21 |
+
align-items: center;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
.top-row ::deep a, .top-row ::deep .btn-link {
|
| 25 |
+
white-space: nowrap;
|
| 26 |
+
margin-left: 1.5rem;
|
| 27 |
+
text-decoration: none;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
| 31 |
+
text-decoration: underline;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
.top-row ::deep a:first-child {
|
| 35 |
+
overflow: hidden;
|
| 36 |
+
text-overflow: ellipsis;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
@media (max-width: 640.98px) {
|
| 40 |
+
.top-row {
|
| 41 |
+
justify-content: space-between;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
.top-row ::deep a, .top-row ::deep .btn-link {
|
| 45 |
+
margin-left: 0;
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
@media (min-width: 641px) {
|
| 50 |
+
.page {
|
| 51 |
+
flex-direction: row;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
.sidebar {
|
| 55 |
+
width: 250px;
|
| 56 |
+
height: 100vh;
|
| 57 |
+
position: sticky;
|
| 58 |
+
top: 0;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
.top-row {
|
| 62 |
+
position: sticky;
|
| 63 |
+
top: 0;
|
| 64 |
+
z-index: 1;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
.top-row.auth ::deep a:first-child {
|
| 68 |
+
flex: 1;
|
| 69 |
+
text-align: right;
|
| 70 |
+
width: 0;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
.top-row, article {
|
| 74 |
+
padding-left: 2rem !important;
|
| 75 |
+
padding-right: 1.5rem !important;
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
#blazor-error-ui {
|
| 80 |
+
background: lightyellow;
|
| 81 |
+
bottom: 0;
|
| 82 |
+
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
| 83 |
+
display: none;
|
| 84 |
+
left: 0;
|
| 85 |
+
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
|
| 86 |
+
position: fixed;
|
| 87 |
+
width: 100%;
|
| 88 |
+
z-index: 1000;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
#blazor-error-ui .dismiss {
|
| 92 |
+
cursor: pointer;
|
| 93 |
+
position: absolute;
|
| 94 |
+
right: 0.75rem;
|
| 95 |
+
top: 0.5rem;
|
| 96 |
+
}
|
LibraryManagement.Frontend/Components/Layout/NavMenu.razor
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div class="top-row ps-3 navbar navbar-dark">
|
| 2 |
+
<div class="container-fluid">
|
| 3 |
+
<a class="navbar-brand" href="">LibraryManagement.Frontend</a>
|
| 4 |
+
</div>
|
| 5 |
+
</div>
|
| 6 |
+
|
| 7 |
+
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
|
| 8 |
+
|
| 9 |
+
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
| 10 |
+
<nav class="flex-column">
|
| 11 |
+
<div class="nav-item px-3">
|
| 12 |
+
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
| 13 |
+
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
|
| 14 |
+
</NavLink>
|
| 15 |
+
</div>
|
| 16 |
+
|
| 17 |
+
<div class="nav-item px-3">
|
| 18 |
+
<NavLink class="nav-link" href="counter">
|
| 19 |
+
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
|
| 20 |
+
</NavLink>
|
| 21 |
+
</div>
|
| 22 |
+
|
| 23 |
+
<div class="nav-item px-3">
|
| 24 |
+
<NavLink class="nav-link" href="weather">
|
| 25 |
+
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
|
| 26 |
+
</NavLink>
|
| 27 |
+
</div>
|
| 28 |
+
</nav>
|
| 29 |
+
</div>
|
| 30 |
+
|
LibraryManagement.Frontend/Components/Layout/NavMenu.razor.css
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.navbar-toggler {
|
| 2 |
+
appearance: none;
|
| 3 |
+
cursor: pointer;
|
| 4 |
+
width: 3.5rem;
|
| 5 |
+
height: 2.5rem;
|
| 6 |
+
color: white;
|
| 7 |
+
position: absolute;
|
| 8 |
+
top: 0.5rem;
|
| 9 |
+
right: 1rem;
|
| 10 |
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
| 11 |
+
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
.navbar-toggler:checked {
|
| 15 |
+
background-color: rgba(255, 255, 255, 0.5);
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
.top-row {
|
| 19 |
+
height: 3.5rem;
|
| 20 |
+
background-color: rgba(0,0,0,0.4);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
.navbar-brand {
|
| 24 |
+
font-size: 1.1rem;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.bi {
|
| 28 |
+
display: inline-block;
|
| 29 |
+
position: relative;
|
| 30 |
+
width: 1.25rem;
|
| 31 |
+
height: 1.25rem;
|
| 32 |
+
margin-right: 0.75rem;
|
| 33 |
+
top: -1px;
|
| 34 |
+
background-size: cover;
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
.bi-house-door-fill-nav-menu {
|
| 38 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
.bi-plus-square-fill-nav-menu {
|
| 42 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
.bi-list-nested-nav-menu {
|
| 46 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
.nav-item {
|
| 50 |
+
font-size: 0.9rem;
|
| 51 |
+
padding-bottom: 0.5rem;
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
.nav-item:first-of-type {
|
| 55 |
+
padding-top: 1rem;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
.nav-item:last-of-type {
|
| 59 |
+
padding-bottom: 1rem;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
.nav-item ::deep .nav-link {
|
| 63 |
+
color: #d7d7d7;
|
| 64 |
+
background: none;
|
| 65 |
+
border: none;
|
| 66 |
+
border-radius: 4px;
|
| 67 |
+
height: 3rem;
|
| 68 |
+
display: flex;
|
| 69 |
+
align-items: center;
|
| 70 |
+
line-height: 3rem;
|
| 71 |
+
width: 100%;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
.nav-item ::deep a.active {
|
| 75 |
+
background-color: rgba(255,255,255,0.37);
|
| 76 |
+
color: white;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
.nav-item ::deep .nav-link:hover {
|
| 80 |
+
background-color: rgba(255,255,255,0.1);
|
| 81 |
+
color: white;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.nav-scrollable {
|
| 85 |
+
display: none;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
.navbar-toggler:checked ~ .nav-scrollable {
|
| 89 |
+
display: block;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
@media (min-width: 641px) {
|
| 93 |
+
.navbar-toggler {
|
| 94 |
+
display: none;
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
.nav-scrollable {
|
| 98 |
+
/* Never collapse the sidebar for wide screens */
|
| 99 |
+
display: block;
|
| 100 |
+
|
| 101 |
+
/* Allow sidebar to scroll for tall menus */
|
| 102 |
+
height: calc(100vh - 3.5rem);
|
| 103 |
+
overflow-y: auto;
|
| 104 |
+
}
|
| 105 |
+
}
|
LibraryManagement.Frontend/Components/Pages/Counter.razor
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@page "/counter"
|
| 2 |
+
@rendermode InteractiveServer
|
| 3 |
+
|
| 4 |
+
<PageTitle>Counter</PageTitle>
|
| 5 |
+
|
| 6 |
+
<h1>Counter</h1>
|
| 7 |
+
|
| 8 |
+
<p role="status">Current count: @currentCount</p>
|
| 9 |
+
|
| 10 |
+
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
| 11 |
+
|
| 12 |
+
@code {
|
| 13 |
+
private int currentCount = 0;
|
| 14 |
+
|
| 15 |
+
private void IncrementCount()
|
| 16 |
+
{
|
| 17 |
+
currentCount++;
|
| 18 |
+
}
|
| 19 |
+
}
|
LibraryManagement/Views/Shared/Error.cshtml → LibraryManagement.Frontend/Components/Pages/Error.razor
RENAMED
|
@@ -1,15 +1,15 @@
|
|
| 1 |
-
@
|
| 2 |
-
@
|
| 3 |
-
|
| 4 |
-
|
| 5 |
|
| 6 |
<h1 class="text-danger">Error.</h1>
|
| 7 |
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
| 8 |
|
| 9 |
-
@if (
|
| 10 |
{
|
| 11 |
<p>
|
| 12 |
-
<strong>Request ID:</strong> <code>@
|
| 13 |
</p>
|
| 14 |
}
|
| 15 |
|
|
@@ -23,3 +23,14 @@
|
|
| 23 |
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
| 24 |
and restarting the app.
|
| 25 |
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@page "/Error"
|
| 2 |
+
@using System.Diagnostics
|
| 3 |
+
|
| 4 |
+
<PageTitle>Error</PageTitle>
|
| 5 |
|
| 6 |
<h1 class="text-danger">Error.</h1>
|
| 7 |
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
| 8 |
|
| 9 |
+
@if (ShowRequestId)
|
| 10 |
{
|
| 11 |
<p>
|
| 12 |
+
<strong>Request ID:</strong> <code>@RequestId</code>
|
| 13 |
</p>
|
| 14 |
}
|
| 15 |
|
|
|
|
| 23 |
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
| 24 |
and restarting the app.
|
| 25 |
</p>
|
| 26 |
+
|
| 27 |
+
@code{
|
| 28 |
+
[CascadingParameter]
|
| 29 |
+
private HttpContext? HttpContext { get; set; }
|
| 30 |
+
|
| 31 |
+
private string? RequestId { get; set; }
|
| 32 |
+
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
| 33 |
+
|
| 34 |
+
protected override void OnInitialized() =>
|
| 35 |
+
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
| 36 |
+
}
|
LibraryManagement.Frontend/Components/Pages/Home.razor
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@page "/"
|
| 2 |
+
|
| 3 |
+
<PageTitle>Home</PageTitle>
|
| 4 |
+
|
| 5 |
+
<h1>Hello, world!</h1>
|
| 6 |
+
|
| 7 |
+
Welcome to your new app.
|
LibraryManagement.Frontend/Components/Pages/Weather.razor
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@page "/weather"
|
| 2 |
+
@attribute [StreamRendering]
|
| 3 |
+
|
| 4 |
+
<PageTitle>Weather</PageTitle>
|
| 5 |
+
|
| 6 |
+
<h1>Weather</h1>
|
| 7 |
+
|
| 8 |
+
<p>This component demonstrates showing data.</p>
|
| 9 |
+
|
| 10 |
+
@if (forecasts == null)
|
| 11 |
+
{
|
| 12 |
+
<p><em>Loading...</em></p>
|
| 13 |
+
}
|
| 14 |
+
else
|
| 15 |
+
{
|
| 16 |
+
<table class="table">
|
| 17 |
+
<thead>
|
| 18 |
+
<tr>
|
| 19 |
+
<th>Date</th>
|
| 20 |
+
<th>Temp. (C)</th>
|
| 21 |
+
<th>Temp. (F)</th>
|
| 22 |
+
<th>Summary</th>
|
| 23 |
+
</tr>
|
| 24 |
+
</thead>
|
| 25 |
+
<tbody>
|
| 26 |
+
@foreach (var forecast in forecasts)
|
| 27 |
+
{
|
| 28 |
+
<tr>
|
| 29 |
+
<td>@forecast.Date.ToShortDateString()</td>
|
| 30 |
+
<td>@forecast.TemperatureC</td>
|
| 31 |
+
<td>@forecast.TemperatureF</td>
|
| 32 |
+
<td>@forecast.Summary</td>
|
| 33 |
+
</tr>
|
| 34 |
+
}
|
| 35 |
+
</tbody>
|
| 36 |
+
</table>
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
@code {
|
| 40 |
+
private WeatherForecast[]? forecasts;
|
| 41 |
+
|
| 42 |
+
protected override async Task OnInitializedAsync()
|
| 43 |
+
{
|
| 44 |
+
// Simulate asynchronous loading to demonstrate streaming rendering
|
| 45 |
+
await Task.Delay(500);
|
| 46 |
+
|
| 47 |
+
var startDate = DateOnly.FromDateTime(DateTime.Now);
|
| 48 |
+
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
|
| 49 |
+
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
| 50 |
+
{
|
| 51 |
+
Date = startDate.AddDays(index),
|
| 52 |
+
TemperatureC = Random.Shared.Next(-20, 55),
|
| 53 |
+
Summary = summaries[Random.Shared.Next(summaries.Length)]
|
| 54 |
+
}).ToArray();
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
private class WeatherForecast
|
| 58 |
+
{
|
| 59 |
+
public DateOnly Date { get; set; }
|
| 60 |
+
public int TemperatureC { get; set; }
|
| 61 |
+
public string? Summary { get; set; }
|
| 62 |
+
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
| 63 |
+
}
|
| 64 |
+
}
|
LibraryManagement.Frontend/Components/Routes.razor
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<Router AppAssembly="typeof(Program).Assembly">
|
| 2 |
+
<Found Context="routeData">
|
| 3 |
+
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
|
| 4 |
+
<FocusOnNavigate RouteData="routeData" Selector="h1" />
|
| 5 |
+
</Found>
|
| 6 |
+
</Router>
|
LibraryManagement.Frontend/Components/_Imports.razor
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@using System.Net.Http
|
| 2 |
+
@using System.Net.Http.Json
|
| 3 |
+
@using Microsoft.AspNetCore.Components.Forms
|
| 4 |
+
@using Microsoft.AspNetCore.Components.Routing
|
| 5 |
+
@using Microsoft.AspNetCore.Components.Web
|
| 6 |
+
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
| 7 |
+
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
| 8 |
+
@using Microsoft.JSInterop
|
| 9 |
+
@using LibraryManagement.Frontend
|
| 10 |
+
@using LibraryManagement.Frontend.Components
|
LibraryManagement/LibraryManagement.csproj → LibraryManagement.Frontend/LibraryManagement.Frontend.csproj
RENAMED
|
File without changes
|
{LibraryManagement → LibraryManagement.Frontend}/Program.cs
RENAMED
|
@@ -1,27 +1,27 @@
|
|
|
|
|
|
|
|
| 1 |
var builder = WebApplication.CreateBuilder(args);
|
| 2 |
|
| 3 |
// Add services to the container.
|
| 4 |
-
builder.Services.
|
|
|
|
| 5 |
|
| 6 |
var app = builder.Build();
|
| 7 |
|
| 8 |
// Configure the HTTP request pipeline.
|
| 9 |
if (!app.Environment.IsDevelopment())
|
| 10 |
{
|
| 11 |
-
app.UseExceptionHandler("/
|
| 12 |
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
| 13 |
app.UseHsts();
|
| 14 |
}
|
| 15 |
|
| 16 |
app.UseHttpsRedirection();
|
| 17 |
-
app.UseStaticFiles();
|
| 18 |
|
| 19 |
-
app.
|
| 20 |
-
|
| 21 |
-
app.UseAuthorization();
|
| 22 |
|
| 23 |
-
app.
|
| 24 |
-
|
| 25 |
-
pattern: "{controller=Home}/{action=Index}/{id?}");
|
| 26 |
|
| 27 |
app.Run();
|
|
|
|
| 1 |
+
using LibraryManagement.Frontend.Components;
|
| 2 |
+
|
| 3 |
var builder = WebApplication.CreateBuilder(args);
|
| 4 |
|
| 5 |
// Add services to the container.
|
| 6 |
+
builder.Services.AddRazorComponents()
|
| 7 |
+
.AddInteractiveServerComponents();
|
| 8 |
|
| 9 |
var app = builder.Build();
|
| 10 |
|
| 11 |
// Configure the HTTP request pipeline.
|
| 12 |
if (!app.Environment.IsDevelopment())
|
| 13 |
{
|
| 14 |
+
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
| 15 |
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
| 16 |
app.UseHsts();
|
| 17 |
}
|
| 18 |
|
| 19 |
app.UseHttpsRedirection();
|
|
|
|
| 20 |
|
| 21 |
+
app.UseStaticFiles();
|
| 22 |
+
app.UseAntiforgery();
|
|
|
|
| 23 |
|
| 24 |
+
app.MapRazorComponents<App>()
|
| 25 |
+
.AddInteractiveServerRenderMode();
|
|
|
|
| 26 |
|
| 27 |
app.Run();
|
LibraryManagement.Frontend/Properties/launchSettings.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema": "http://json.schemastore.org/launchsettings.json",
|
| 3 |
+
"iisSettings": {
|
| 4 |
+
"windowsAuthentication": false,
|
| 5 |
+
"anonymousAuthentication": true,
|
| 6 |
+
"iisExpress": {
|
| 7 |
+
"applicationUrl": "http://localhost:63301",
|
| 8 |
+
"sslPort": 44390
|
| 9 |
+
}
|
| 10 |
+
},
|
| 11 |
+
"profiles": {
|
| 12 |
+
"http": {
|
| 13 |
+
"commandName": "Project",
|
| 14 |
+
"dotnetRunMessages": true,
|
| 15 |
+
"launchBrowser": true,
|
| 16 |
+
"applicationUrl": "http://localhost:5008",
|
| 17 |
+
"environmentVariables": {
|
| 18 |
+
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
"https": {
|
| 22 |
+
"commandName": "Project",
|
| 23 |
+
"dotnetRunMessages": true,
|
| 24 |
+
"launchBrowser": true,
|
| 25 |
+
"applicationUrl": "https://localhost:7187;http://localhost:5008",
|
| 26 |
+
"environmentVariables": {
|
| 27 |
+
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 28 |
+
}
|
| 29 |
+
},
|
| 30 |
+
"IIS Express": {
|
| 31 |
+
"commandName": "IISExpress",
|
| 32 |
+
"launchBrowser": true,
|
| 33 |
+
"environmentVariables": {
|
| 34 |
+
"ASPNETCORE_ENVIRONMENT": "Development"
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
LibraryManagement.Frontend/appsettings.Development.json
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"Logging": {
|
| 3 |
+
"LogLevel": {
|
| 4 |
+
"Default": "Information",
|
| 5 |
+
"Microsoft.AspNetCore": "Warning"
|
| 6 |
+
}
|
| 7 |
+
}
|
| 8 |
+
}
|
{LibraryManagement → LibraryManagement.Frontend}/appsettings.json
RENAMED
|
File without changes
|
LibraryManagement.Frontend/wwwroot/app.css
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
html, body {
|
| 2 |
+
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
| 3 |
+
}
|
| 4 |
+
|
| 5 |
+
a, .btn-link {
|
| 6 |
+
color: #006bb7;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
.btn-primary {
|
| 10 |
+
color: #fff;
|
| 11 |
+
background-color: #1b6ec2;
|
| 12 |
+
border-color: #1861ac;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
|
| 16 |
+
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
.content {
|
| 20 |
+
padding-top: 1.1rem;
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
h1:focus {
|
| 24 |
+
outline: none;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
.valid.modified:not([type=checkbox]) {
|
| 28 |
+
outline: 1px solid #26b050;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
.invalid {
|
| 32 |
+
outline: 1px solid #e50000;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
.validation-message {
|
| 36 |
+
color: #e50000;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
.blazor-error-boundary {
|
| 40 |
+
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
| 41 |
+
padding: 1rem 1rem 1rem 3.7rem;
|
| 42 |
+
color: white;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
.blazor-error-boundary::after {
|
| 46 |
+
content: "An error has occurred."
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
.darker-border-checkbox.form-check-input {
|
| 50 |
+
border-color: #929292;
|
| 51 |
+
}
|
{LibraryManagement/wwwroot/lib/bootstrap/dist/css → LibraryManagement.Frontend/wwwroot/bootstrap}/bootstrap.min.css
RENAMED
|
File without changes
|
{LibraryManagement/wwwroot/lib/bootstrap/dist/css → LibraryManagement.Frontend/wwwroot/bootstrap}/bootstrap.min.css.map
RENAMED
|
File without changes
|
LibraryManagement.Frontend/wwwroot/favicon.png
ADDED
|
|
LibraryManagement.slnx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
| 1 |
<Solution>
|
| 2 |
-
<Project Path="
|
|
|
|
|
|
|
| 3 |
</Solution>
|
|
|
|
| 1 |
<Solution>
|
| 2 |
+
<Project Path="Database/Database.csproj" Id="1ec23dc7-355d-4456-a503-cfd0fb81cd5c" />
|
| 3 |
+
<Project Path="LibraryManagement.Backend/LibraryManagement.Backend.csproj" Id="82e311b1-ce76-4235-9d5f-0d06a942d038" />
|
| 4 |
+
<Project Path="LibraryManagement.Frontend/LibraryManagement.Frontend.csproj" Id="4209546e-83fd-4d5c-8707-47c86053d9a8" />
|
| 5 |
</Solution>
|
LibraryManagement/Controllers/HomeController.cs
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
using LibraryManagement.Models;
|
| 2 |
-
using Microsoft.AspNetCore.Mvc;
|
| 3 |
-
using System.Diagnostics;
|
| 4 |
-
|
| 5 |
-
namespace LibraryManagement.Controllers
|
| 6 |
-
{
|
| 7 |
-
public class HomeController : Controller
|
| 8 |
-
{
|
| 9 |
-
private readonly ILogger<HomeController> _logger;
|
| 10 |
-
|
| 11 |
-
public HomeController(ILogger<HomeController> logger)
|
| 12 |
-
{
|
| 13 |
-
_logger = logger;
|
| 14 |
-
}
|
| 15 |
-
|
| 16 |
-
public IActionResult Index()
|
| 17 |
-
{
|
| 18 |
-
return View();
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
public IActionResult Privacy()
|
| 22 |
-
{
|
| 23 |
-
return View();
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
| 27 |
-
public IActionResult Error()
|
| 28 |
-
{
|
| 29 |
-
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
| 30 |
-
}
|
| 31 |
-
}
|
| 32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LibraryManagement/Models/ErrorViewModel.cs
DELETED
|
@@ -1,9 +0,0 @@
|
|
| 1 |
-
namespace LibraryManagement.Models
|
| 2 |
-
{
|
| 3 |
-
public class ErrorViewModel
|
| 4 |
-
{
|
| 5 |
-
public string? RequestId { get; set; }
|
| 6 |
-
|
| 7 |
-
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
| 8 |
-
}
|
| 9 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LibraryManagement/Views/Home/Index.cshtml
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
@{
|
| 2 |
-
ViewData["Title"] = "Home Page";
|
| 3 |
-
}
|
| 4 |
-
|
| 5 |
-
<div class="text-center">
|
| 6 |
-
<h1 class="display-4">Welcome</h1>
|
| 7 |
-
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
| 8 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LibraryManagement/Views/Home/Privacy.cshtml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
@{
|
| 2 |
-
ViewData["Title"] = "Privacy Policy";
|
| 3 |
-
}
|
| 4 |
-
<h1>@ViewData["Title"]</h1>
|
| 5 |
-
|
| 6 |
-
<p>Use this page to detail your site's privacy policy.</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LibraryManagement/Views/Shared/_Layout.cshtml
DELETED
|
@@ -1,49 +0,0 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
-
<html lang="en">
|
| 3 |
-
<head>
|
| 4 |
-
<meta charset="utf-8" />
|
| 5 |
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
-
<title>@ViewData["Title"] - LibraryManagement</title>
|
| 7 |
-
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
| 8 |
-
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
| 9 |
-
<link rel="stylesheet" href="~/LibraryManagement.styles.css" asp-append-version="true" />
|
| 10 |
-
</head>
|
| 11 |
-
<body>
|
| 12 |
-
<header>
|
| 13 |
-
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
| 14 |
-
<div class="container-fluid">
|
| 15 |
-
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">LibraryManagement</a>
|
| 16 |
-
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
| 17 |
-
aria-expanded="false" aria-label="Toggle navigation">
|
| 18 |
-
<span class="navbar-toggler-icon"></span>
|
| 19 |
-
</button>
|
| 20 |
-
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
|
| 21 |
-
<ul class="navbar-nav flex-grow-1">
|
| 22 |
-
<li class="nav-item">
|
| 23 |
-
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
|
| 24 |
-
</li>
|
| 25 |
-
<li class="nav-item">
|
| 26 |
-
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
| 27 |
-
</li>
|
| 28 |
-
</ul>
|
| 29 |
-
</div>
|
| 30 |
-
</div>
|
| 31 |
-
</nav>
|
| 32 |
-
</header>
|
| 33 |
-
<div class="container">
|
| 34 |
-
<main role="main" class="pb-3">
|
| 35 |
-
@RenderBody()
|
| 36 |
-
</main>
|
| 37 |
-
</div>
|
| 38 |
-
|
| 39 |
-
<footer class="border-top footer text-muted">
|
| 40 |
-
<div class="container">
|
| 41 |
-
© 2026 - LibraryManagement - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
|
| 42 |
-
</div>
|
| 43 |
-
</footer>
|
| 44 |
-
<script src="~/lib/jquery/dist/jquery.min.js"></script>
|
| 45 |
-
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
|
| 46 |
-
<script src="~/js/site.js" asp-append-version="true"></script>
|
| 47 |
-
@await RenderSectionAsync("Scripts", required: false)
|
| 48 |
-
</body>
|
| 49 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LibraryManagement/Views/Shared/_Layout.cshtml.css
DELETED
|
@@ -1,48 +0,0 @@
|
|
| 1 |
-
/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
| 2 |
-
for details on configuring this project to bundle and minify static web assets. */
|
| 3 |
-
|
| 4 |
-
a.navbar-brand {
|
| 5 |
-
white-space: normal;
|
| 6 |
-
text-align: center;
|
| 7 |
-
word-break: break-all;
|
| 8 |
-
}
|
| 9 |
-
|
| 10 |
-
a {
|
| 11 |
-
color: #0077cc;
|
| 12 |
-
}
|
| 13 |
-
|
| 14 |
-
.btn-primary {
|
| 15 |
-
color: #fff;
|
| 16 |
-
background-color: #1b6ec2;
|
| 17 |
-
border-color: #1861ac;
|
| 18 |
-
}
|
| 19 |
-
|
| 20 |
-
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
| 21 |
-
color: #fff;
|
| 22 |
-
background-color: #1b6ec2;
|
| 23 |
-
border-color: #1861ac;
|
| 24 |
-
}
|
| 25 |
-
|
| 26 |
-
.border-top {
|
| 27 |
-
border-top: 1px solid #e5e5e5;
|
| 28 |
-
}
|
| 29 |
-
.border-bottom {
|
| 30 |
-
border-bottom: 1px solid #e5e5e5;
|
| 31 |
-
}
|
| 32 |
-
|
| 33 |
-
.box-shadow {
|
| 34 |
-
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
|
| 35 |
-
}
|
| 36 |
-
|
| 37 |
-
button.accept-policy {
|
| 38 |
-
font-size: 1rem;
|
| 39 |
-
line-height: inherit;
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
.footer {
|
| 43 |
-
position: absolute;
|
| 44 |
-
bottom: 0;
|
| 45 |
-
width: 100%;
|
| 46 |
-
white-space: nowrap;
|
| 47 |
-
line-height: 60px;
|
| 48 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LibraryManagement/Views/Shared/_ValidationScriptsPartial.cshtml
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
|
| 2 |
-
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
|
|
|
|
|
|
|
|
|
LibraryManagement/Views/_ViewImports.cshtml
DELETED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
@using LibraryManagement
|
| 2 |
-
@using LibraryManagement.Models
|
| 3 |
-
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
|
|
|
|
|
|
|
|
|
|
|