From 6efa8aba8a14b8051150a8ed115dc04825e5123a Mon Sep 17 00:00:00 2001 From: florpan Date: Tue, 16 Sep 2025 00:19:54 +0200 Subject: [PATCH] blandat.. fixar o nullcheckar. filtreringar --- Frontend/src/components/BookCard.tsx | 2 +- Frontend/src/components/SeriesList.tsx | 93 ++++++++++++++++----- Frontend/src/styles/styles.css | 4 + Frontend/src/types/types.ts | 2 + Server/Business/Services/CalibreService.cs | 4 + Server/Business/Services/DatabaseService.cs | 45 +++++----- Server/Controllers/BibblanController.cs | 3 + Server/Controllers/CalibreController.cs | 2 + Server/ViewModels/SeriesVm.cs | 2 + 9 files changed, 114 insertions(+), 43 deletions(-) diff --git a/Frontend/src/components/BookCard.tsx b/Frontend/src/components/BookCard.tsx index 3e597f8..2b87bc8 100644 --- a/Frontend/src/components/BookCard.tsx +++ b/Frontend/src/components/BookCard.tsx @@ -5,7 +5,7 @@ import { Card } from "solid-bootstrap"; const BookCard: Component<{ book: book }> = (props: { book: book }) => { return ( - + { const [series, setSeries] = createSignal([]); const [query, setQuery] = createSignal(""); + const [selected, setSelected] = createSignal(-1); const update = (query: string) => { setQuery(query); @@ -40,6 +41,30 @@ const SeriesList: Component = () => { return d?.toISOString().substr(0, 10) ?? ""; }; + function extendBook(item: listBook, seriesName: string): book { + return { + ...item, + + author: item.authorName, + comments: "", + + language: "", + path: item.path, + hasCover: item.hasCover, + formats: [], + seriesName, + seriesNumber: item.seriesIndex, + }; + } + + function orderBySeriesIndex(books: listBook[]): listBook[] { + return [...books].sort((a, b) => { + const indexA = a.seriesIndex ?? 0; + const indexB = b.seriesIndex ?? 0; + return indexA - indexB; + }); + } + return (

Series!

@@ -49,7 +74,7 @@ const SeriesList: Component = () => { placeholder="Search..." onInput={(e) => update(e.currentTarget.value)} /> - +
@@ -57,27 +82,53 @@ const SeriesList: Component = () => { + {(item) => ( - - - - - - - + <> + + + + + + + + + )} diff --git a/Frontend/src/styles/styles.css b/Frontend/src/styles/styles.css index eb998e9..0409c86 100644 --- a/Frontend/src/styles/styles.css +++ b/Frontend/src/styles/styles.css @@ -6,3 +6,7 @@ aspect-ratio: 3 / 4; } } + +#popover-series { + --bs-popover-max-width: 80%; +} diff --git a/Frontend/src/types/types.ts b/Frontend/src/types/types.ts index a7bf30f..2766f03 100644 --- a/Frontend/src/types/types.ts +++ b/Frontend/src/types/types.ts @@ -23,6 +23,8 @@ interface listBook { title: string; authorId: number; authorName: string; + path: string; + hasCover: boolean; pubDate: Date; seriesIndex: number; } diff --git a/Server/Business/Services/CalibreService.cs b/Server/Business/Services/CalibreService.cs index 8063917..e570e2a 100644 --- a/Server/Business/Services/CalibreService.cs +++ b/Server/Business/Services/CalibreService.cs @@ -1,5 +1,6 @@ using Bibblan.Models; using Bibblan.ViewModels; +using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.Extensions.Options; namespace Bibblan.Business.Services @@ -17,6 +18,9 @@ namespace Bibblan.Business.Services public byte[] Cover(string path) { var fullPath = Path.Combine(_options.CalibreRoot, path, "cover.jpg"); + if (!File.Exists(fullPath)) + return null; + return File.ReadAllBytes(fullPath); } diff --git a/Server/Business/Services/DatabaseService.cs b/Server/Business/Services/DatabaseService.cs index 58b981c..3702ad1 100644 --- a/Server/Business/Services/DatabaseService.cs +++ b/Server/Business/Services/DatabaseService.cs @@ -13,18 +13,16 @@ namespace Bibblan.Business.Services public string? Query; } - public class DatabaseService + public class DatabaseService(IOptions options) { - BibblanOptions settings; - public DatabaseService(IOptions options) { - settings = options.Value; - } + readonly BibblanOptions settings = options.Value; public IEnumerable GetBooks(int count, BookFilter filter = null) { var conn = new NpgsqlConnection(settings.BibblanConnection); var query = "select * from books"; - if(filter != null) + object parameters = null; + if (filter != null) { query += " where "; if(filter.Author != null) @@ -33,46 +31,48 @@ namespace Bibblan.Business.Services } else if(!String.IsNullOrWhiteSpace(query)) { filter.Query = filter.Query.ToLowerInvariant(); - query += $"lower(title) like '%{filter.Query}%' or lower(author_sort) like '%{filter.Query}%'"; + query += $"lower(title) like @query or lower(author_sort) like @query"; + parameters = new { query = "%" + filter.Query + "%" }; } } - return conn.Query(query).Take(count).ToList(); + return conn.Query(query, parameters).Take(count).ToList(); } public IEnumerable GetAuthors(int count, BookFilter filter = null) { var query = "select a.id, a.name, count(bal.*) as bookcount\r\nfrom authors a\r\nleft join books_authors_link bal on a.id = bal.author\r\ngroup by a.id , a.name "; - if(!String.IsNullOrWhiteSpace(filter?.Query)) + object parameters = null; + if (!String.IsNullOrWhiteSpace(filter?.Query)) { filter.Query = filter.Query.ToLowerInvariant(); - query += $" having lower(a.name) like '%{filter.Query}%'"; + query += $" having lower(a.name) like @query"; + parameters = new { query = "%" + filter.Query + "%" }; } var conn = new NpgsqlConnection(settings.BibblanConnection); - return conn.Query(query).Take(count).ToList(); + return conn.Query(query, parameters).Take(count).ToList(); } internal List GetSeries(int count, BookFilter? filter) { var query = @"select s.id as seriesid, s.name as seriesname, b.*, a.* - --b.id as bookid, b.title, b.pubdate, b.series_index, - --a.id as authorid, a.name as authorname from series s inner join books_series_link bsl on s.id = bsl.series inner join books b on bsl.book = b.id inner join books_authors_link bal on b.id = bal.book inner join authors a on bal.author = a.id"; + object parameters = null; if (!String.IsNullOrWhiteSpace(filter?.Query)) { filter.Query = filter.Query?.ToLowerInvariant().Trim() ?? ""; - query += $" where lower(s.name) like '%{filter.Query}%'"; + query += $" where lower(s.name) like @query"; + parameters = new { query = "%" + filter.Query + "%" }; } var conn = new NpgsqlConnection(settings.BibblanConnection); var lookup = new Dictionary(); conn.Query(query, (s,b,a) => { - SeriesVm svm; - if (!lookup.TryGetValue(s.Id, out svm)) + if (!lookup.TryGetValue(s.Id, out SeriesVm svm)) { lookup.Add(s.Id, svm = s); } @@ -82,13 +82,14 @@ namespace Bibblan.Business.Services AuthorName = a.Name, Id = b.Id, PubDate = b.Pubdate, + HasCover = b.HasCover, + Path = b.Path, SeriesIndex = b.SeriesIndex, Title = b.Title - }); return svm; - }, splitOn: "id").Take(count).ToList(); - return lookup.Values.AsList(); + }, splitOn: "id", param: parameters); + return lookup.Values.Take(count).ToList(); } internal List GetTags(BookFilter? filter) @@ -97,13 +98,15 @@ namespace Bibblan.Business.Services inner join books_tags_link btl on t.id = btl.tag group by t.id, t.name order by bookcount desc"; + object parameters = null; if (!String.IsNullOrWhiteSpace(filter?.Query)) { filter.Query = filter.Query.ToLowerInvariant(); - query += $" having lower(name) like '%{filter.Query}%'"; + query += $" having lower(name) like @query"; + parameters = new { query = "%" + filter.Query + "%" }; } var conn = new NpgsqlConnection(settings.BibblanConnection); - return conn.Query(query).ToList(); + return conn.Query(query,parameters).ToList(); } } } diff --git a/Server/Controllers/BibblanController.cs b/Server/Controllers/BibblanController.cs index 694836d..0dfb6f6 100644 --- a/Server/Controllers/BibblanController.cs +++ b/Server/Controllers/BibblanController.cs @@ -21,6 +21,9 @@ namespace Bibblan.Controllers { //TODO: Bör kanske inte gå direkt mot calibres filer.. var bytes = _calibre.Cover(path); + if (bytes == null) + return NotFound(); + return File(bytes, "image/jpeg", true); } diff --git a/Server/Controllers/CalibreController.cs b/Server/Controllers/CalibreController.cs index b9b86a8..a9320af 100644 --- a/Server/Controllers/CalibreController.cs +++ b/Server/Controllers/CalibreController.cs @@ -31,6 +31,8 @@ namespace Bibblan.Controllers public IActionResult GetCover(string path) { var bytes = _service.Cover(path); + if (bytes == null) + return NotFound(); return File(bytes, "image/jpeg", true); } diff --git a/Server/ViewModels/SeriesVm.cs b/Server/ViewModels/SeriesVm.cs index 412ccd5..fbf70ca 100644 --- a/Server/ViewModels/SeriesVm.cs +++ b/Server/ViewModels/SeriesVm.cs @@ -17,6 +17,8 @@ namespace Bibblan.ViewModels public double SeriesIndex { get; set; } public long AuthorId { get; set; } public string AuthorName { get; set; } + public bool HasCover { get; set; } + public string Path { get; set; } }
IDAuthor Book Count Published
{item.id}{item.name} - - {(author) => ( - {author.name} - )} - - {item.books.length} - {minDate(item.books) ?? "N/A"} -{" "} - {maxDate(item.books) ?? "N/A"} -
{item.id}{item.name} + + {(author) => ( + {author.name} + )} + + {item.books.length} + {minDate(item.books) ?? "N/A"} -{" "} + {maxDate(item.books) ?? "N/A"} + + + {item.name} + +
+ + {(b) => ( + + )} + +
+
+ + } + > + +
+