diff --git a/OliverBooth/Data/Web/Book.cs b/OliverBooth/Data/Web/Book.cs new file mode 100644 index 0000000..385e870 --- /dev/null +++ b/OliverBooth/Data/Web/Book.cs @@ -0,0 +1,19 @@ +namespace OliverBooth.Data.Web; + +/// +/// Represents a book. +/// +internal sealed class Book : IBook +{ + /// + public string Author { get; } + + /// + public string Isbn { get; } + + /// + public BookState State { get; } + + /// + public string Title { get; } +} diff --git a/OliverBooth/Data/Web/BookState.cs b/OliverBooth/Data/Web/BookState.cs new file mode 100644 index 0000000..637415a --- /dev/null +++ b/OliverBooth/Data/Web/BookState.cs @@ -0,0 +1,22 @@ +namespace OliverBooth.Data.Web; + +/// +/// Represents the state of a book. +/// +public enum BookState +{ + /// + /// The book has been read and finished. + /// + Read, + + /// + /// The book is on the current reading list. + /// + Reading, + + /// + /// The book is on a future reading list. + /// + PlanToRead +} \ No newline at end of file diff --git a/OliverBooth/Data/Web/Configuration/BookConfiguration.cs b/OliverBooth/Data/Web/Configuration/BookConfiguration.cs new file mode 100644 index 0000000..f27a1d2 --- /dev/null +++ b/OliverBooth/Data/Web/Configuration/BookConfiguration.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace OliverBooth.Data.Web.Configuration; + +/// +/// Represents the configuration for the entity. +/// +internal sealed class BookConfiguration : IEntityTypeConfiguration +{ + /// + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("Book"); + builder.HasKey(entry => entry.Isbn); + + builder.Property(entry => entry.Isbn).IsRequired(); + builder.Property(entry => entry.Title).IsRequired(); + builder.Property(entry => entry.Author).IsRequired(); + builder.Property(entry => entry.State).HasConversion>().IsRequired(); + } +} diff --git a/OliverBooth/Data/Web/IBook.cs b/OliverBooth/Data/Web/IBook.cs new file mode 100644 index 0000000..1c968cf --- /dev/null +++ b/OliverBooth/Data/Web/IBook.cs @@ -0,0 +1,31 @@ +namespace OliverBooth.Data.Web; + +/// +/// Represents a book. +/// +public interface IBook +{ + /// + /// Gets the author of the book. + /// + /// The author of the book. + string Author { get; } + + /// + /// Gets the ISBN of the book. + /// + /// The ISBN of the book. + string Isbn { get; } + + /// + /// Gets the state of the book. + /// + /// The state of the book. + BookState State { get; } + + /// + /// Gets the title of the book. + /// + /// The title of the book. + string Title { get; } +} \ No newline at end of file diff --git a/OliverBooth/Data/Web/WebContext.cs b/OliverBooth/Data/Web/WebContext.cs index 1f5a914..ab1141e 100644 --- a/OliverBooth/Data/Web/WebContext.cs +++ b/OliverBooth/Data/Web/WebContext.cs @@ -19,6 +19,12 @@ internal sealed class WebContext : DbContext _configuration = configuration; } + /// + /// Gets the collection of books in the reading list. + /// + /// The collection of books. + public DbSet Books { get; private set; } = null!; + /// /// Gets the collection of projects in the database. /// @@ -47,6 +53,7 @@ internal sealed class WebContext : DbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.ApplyConfiguration(new BookConfiguration()); modelBuilder.ApplyConfiguration(new ProjectConfiguration()); modelBuilder.ApplyConfiguration(new TemplateConfiguration()); modelBuilder.ApplyConfiguration(new SiteConfigurationConfiguration()); diff --git a/OliverBooth/Pages/Books.cshtml b/OliverBooth/Pages/Books.cshtml new file mode 100644 index 0000000..6d73685 --- /dev/null +++ b/OliverBooth/Pages/Books.cshtml @@ -0,0 +1,90 @@ +@page +@using OliverBooth.Data.Web +@model OliverBooth.Pages.Books +@{ + ViewData["Title"] = "Reading List"; +} + +

Reading List

+

+ This is a list of the books I've read, I'm currently reading, or that I plan to read. Not every book is listed here, + but I will update this list as I try to remember what it is I've read in the past. +

+

+ This list is also available on Goodreads. +

+ +

Currently Reading

+ + + + + + + + + + + @foreach (IBook book in Model.CurrentlyReading.OrderBy(b => b.Author).ThenBy(b => b.Title)) + { + + + + + + } + +
TitleAuthorISBN
+ Book Cover + @book.Title.Trim() + @book.Author.Trim()@book.Isbn.Trim()
+ +

Plan to Read

+ + + + + + + + + + + @foreach (IBook book in Model.PlanToRead.OrderBy(b => b.Author).ThenBy(b => b.Title)) + { + + + + + + } + +
TitleAuthorISBN
+ Book Cover + @book.Title.Trim() + @book.Author.Trim()@book.Isbn.Trim()
+ +

Read

+ + + + + + + + + + + @foreach (IBook book in Model.Read.OrderBy(b => b.Author).ThenBy(b => b.Title)) + { + + + + + + } + +
TitleAuthorISBN
+ Book Cover + @book.Title.Trim() + @book.Author.Trim()@book.Isbn.Trim()
\ No newline at end of file diff --git a/OliverBooth/Pages/Books.cshtml.cs b/OliverBooth/Pages/Books.cshtml.cs new file mode 100644 index 0000000..6cbc5af --- /dev/null +++ b/OliverBooth/Pages/Books.cshtml.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; +using OliverBooth.Data.Web; +using OliverBooth.Services; + +namespace OliverBooth.Pages; + +public class Books : PageModel +{ + private readonly IReadingListService _readingListService; + + public Books(IReadingListService readingListService) + { + _readingListService = readingListService; + } + + /// + /// Gets the books currently being read. + /// + /// The books currently being read. + public IReadOnlyCollection CurrentlyReading { get; private set; } = ArraySegment.Empty; + + /// + /// Gets the books that are planned to be read. + /// + /// The books that are planned to be read. + public IReadOnlyCollection PlanToRead { get; private set; } = ArraySegment.Empty; + + /// + /// Gets the books that have been read. + /// + /// The books that have been read. + public IReadOnlyCollection Read { get; private set; } = ArraySegment.Empty; + + public void OnGet() + { + CurrentlyReading = _readingListService.GetBooks(BookState.Reading); + PlanToRead = _readingListService.GetBooks(BookState.PlanToRead); + Read = _readingListService.GetBooks(BookState.Read); + } +} diff --git a/OliverBooth/Program.cs b/OliverBooth/Program.cs index beb0140..5dae0a1 100644 --- a/OliverBooth/Program.cs +++ b/OliverBooth/Program.cs @@ -36,6 +36,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); builder.Services.AddRazorPages().AddRazorRuntimeCompilation(); builder.Services.AddControllersWithViews(); builder.Services.AddRouting(options => options.LowercaseUrls = true); diff --git a/src/scss/app.scss b/src/scss/app.scss index 6b19929..c1b1eec 100644 --- a/src/scss/app.scss +++ b/src/scss/app.scss @@ -300,4 +300,15 @@ a.brand-linkedin { &:hover { color: #fff; } +} + +table.reading-list { + th,td { + vertical-align: middle; + } +} + +.book-cover { + width: 50px; + vertical-align: middle; } \ No newline at end of file