feat: adding reading list
This commit is contained in:
parent
3186ce9b50
commit
5cb61d275d
|
@ -0,0 +1,19 @@
|
|||
namespace OliverBooth.Data.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a book.
|
||||
/// </summary>
|
||||
internal sealed class Book : IBook
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Author { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Isbn { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public BookState State { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Title { get; }
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
namespace OliverBooth.Data.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the state of a book.
|
||||
/// </summary>
|
||||
public enum BookState
|
||||
{
|
||||
/// <summary>
|
||||
/// The book has been read and finished.
|
||||
/// </summary>
|
||||
Read,
|
||||
|
||||
/// <summary>
|
||||
/// The book is on the current reading list.
|
||||
/// </summary>
|
||||
Reading,
|
||||
|
||||
/// <summary>
|
||||
/// The book is on a future reading list.
|
||||
/// </summary>
|
||||
PlanToRead
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace OliverBooth.Data.Web.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the configuration for the <see cref="Book" /> entity.
|
||||
/// </summary>
|
||||
internal sealed class BookConfiguration : IEntityTypeConfiguration<Book>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void Configure(EntityTypeBuilder<Book> 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<EnumToStringConverter<BookState>>().IsRequired();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
namespace OliverBooth.Data.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a book.
|
||||
/// </summary>
|
||||
public interface IBook
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the author of the book.
|
||||
/// </summary>
|
||||
/// <value>The author of the book.</value>
|
||||
string Author { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ISBN of the book.
|
||||
/// </summary>
|
||||
/// <value>The ISBN of the book.</value>
|
||||
string Isbn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state of the book.
|
||||
/// </summary>
|
||||
/// <value>The state of the book.</value>
|
||||
BookState State { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the title of the book.
|
||||
/// </summary>
|
||||
/// <value>The title of the book.</value>
|
||||
string Title { get; }
|
||||
}
|
|
@ -19,6 +19,12 @@ internal sealed class WebContext : DbContext
|
|||
_configuration = configuration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of books in the reading list.
|
||||
/// </summary>
|
||||
/// <value>The collection of books.</value>
|
||||
public DbSet<Book> Books { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of projects in the database.
|
||||
/// </summary>
|
||||
|
@ -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());
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
@page
|
||||
@using OliverBooth.Data.Web
|
||||
@model OliverBooth.Pages.Books
|
||||
@{
|
||||
ViewData["Title"] = "Reading List";
|
||||
}
|
||||
|
||||
<h1 class="display-4">Reading List</h1>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
This list is also available on <a href="https://www.goodreads.com/review/list/145592619">Goodreads</a>.
|
||||
</p>
|
||||
|
||||
<p class="lead">Currently Reading</p>
|
||||
<table class="table reading-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Title</th>
|
||||
<th style="width: 25%">Author</th>
|
||||
<th style="width: 25%">ISBN</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach (IBook book in Model.CurrentlyReading.OrderBy(b => b.Author).ThenBy(b => b.Title))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<img class="book-cover" src="https://cdn.olivr.me/img/books/@(book.Isbn.Trim()).jpg" alt="Book Cover">
|
||||
@book.Title.Trim()
|
||||
</td>
|
||||
<td>@book.Author.Trim()</td>
|
||||
<td>@book.Isbn.Trim()</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="lead">Plan to Read</p>
|
||||
<table class="table reading-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Title</th>
|
||||
<th style="width: 25%">Author</th>
|
||||
<th style="width: 25%">ISBN</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach (IBook book in Model.PlanToRead.OrderBy(b => b.Author).ThenBy(b => b.Title))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<img class="book-cover" src="https://cdn.olivr.me/img/books/@(book.Isbn.Trim()).jpg" alt="Book Cover">
|
||||
@book.Title.Trim()
|
||||
</td>
|
||||
<td>@book.Author.Trim()</td>
|
||||
<td>@book.Isbn.Trim()</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p class="lead">Read</p>
|
||||
<table class="table reading-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Title</th>
|
||||
<th style="width: 25%">Author</th>
|
||||
<th style="width: 25%">ISBN</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
@foreach (IBook book in Model.Read.OrderBy(b => b.Author).ThenBy(b => b.Title))
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
<img class="book-cover" src="https://cdn.olivr.me/img/books/@(book.Isbn.Trim()).jpg" alt="Book Cover">
|
||||
@book.Title.Trim()
|
||||
</td>
|
||||
<td>@book.Author.Trim()</td>
|
||||
<td>@book.Isbn.Trim()</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the books currently being read.
|
||||
/// </summary>
|
||||
/// <value>The books currently being read.</value>
|
||||
public IReadOnlyCollection<IBook> CurrentlyReading { get; private set; } = ArraySegment<IBook>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the books that are planned to be read.
|
||||
/// </summary>
|
||||
/// <value>The books that are planned to be read.</value>
|
||||
public IReadOnlyCollection<IBook> PlanToRead { get; private set; } = ArraySegment<IBook>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the books that have been read.
|
||||
/// </summary>
|
||||
/// <value>The books that have been read.</value>
|
||||
public IReadOnlyCollection<IBook> Read { get; private set; } = ArraySegment<IBook>.Empty;
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
CurrentlyReading = _readingListService.GetBooks(BookState.Reading);
|
||||
PlanToRead = _readingListService.GetBooks(BookState.PlanToRead);
|
||||
Read = _readingListService.GetBooks(BookState.Read);
|
||||
}
|
||||
}
|
|
@ -36,6 +36,7 @@ builder.Services.AddSingleton<ITemplateService, TemplateService>();
|
|||
builder.Services.AddSingleton<IBlogPostService, BlogPostService>();
|
||||
builder.Services.AddSingleton<IBlogUserService, BlogUserService>();
|
||||
builder.Services.AddSingleton<IProjectService, ProjectService>();
|
||||
builder.Services.AddSingleton<IReadingListService, ReadingListService>();
|
||||
builder.Services.AddRazorPages().AddRazorRuntimeCompilation();
|
||||
builder.Services.AddControllersWithViews();
|
||||
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
||||
|
|
|
@ -301,3 +301,14 @@ a.brand-linkedin {
|
|||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
table.reading-list {
|
||||
th,td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.book-cover {
|
||||
width: 50px;
|
||||
vertical-align: middle;
|
||||
}
|
Loading…
Reference in New Issue