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;
|
_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>
|
/// <summary>
|
||||||
/// Gets the collection of projects in the database.
|
/// Gets the collection of projects in the database.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -47,6 +53,7 @@ internal sealed class WebContext : DbContext
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
|
modelBuilder.ApplyConfiguration(new BookConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
|
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new TemplateConfiguration());
|
modelBuilder.ApplyConfiguration(new TemplateConfiguration());
|
||||||
modelBuilder.ApplyConfiguration(new SiteConfigurationConfiguration());
|
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<IBlogPostService, BlogPostService>();
|
||||||
builder.Services.AddSingleton<IBlogUserService, BlogUserService>();
|
builder.Services.AddSingleton<IBlogUserService, BlogUserService>();
|
||||||
builder.Services.AddSingleton<IProjectService, ProjectService>();
|
builder.Services.AddSingleton<IProjectService, ProjectService>();
|
||||||
|
builder.Services.AddSingleton<IReadingListService, ReadingListService>();
|
||||||
builder.Services.AddRazorPages().AddRazorRuntimeCompilation();
|
builder.Services.AddRazorPages().AddRazorRuntimeCompilation();
|
||||||
builder.Services.AddControllersWithViews();
|
builder.Services.AddControllersWithViews();
|
||||||
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
builder.Services.AddRouting(options => options.LowercaseUrls = true);
|
||||||
|
|
|
@ -300,4 +300,15 @@ a.brand-linkedin {
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.reading-list {
|
||||||
|
th,td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-cover {
|
||||||
|
width: 50px;
|
||||||
|
vertical-align: middle;
|
||||||
}
|
}
|
Loading…
Reference in New Issue