diff --git a/Frontend/package.json b/Frontend/package.json index 4632e9e..b59efe1 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -17,6 +17,7 @@ "vite-plugin-solid": "^2.11.8" }, "dependencies": { + "@solidjs/router": "^0.15.3", "bootstrap": "^5.3.8", "solid-bootstrap": "^1.0.21", "solid-js": "^1.9.9" diff --git a/Frontend/src/App.tsx b/Frontend/src/App.tsx index fb1e413..e165f62 100644 --- a/Frontend/src/App.tsx +++ b/Frontend/src/App.tsx @@ -1,32 +1,54 @@ +import { Router, Route } from "@solidjs/router"; import type { Component } from "solid-js"; -import { createSignal, For, onMount } from "solid-js"; -import CalibreService from "./services/calibreservice"; -import { book } from "./types/calibretypes"; -import BookCard from "./components/BookCard"; +import Home from "./components/Home"; +import BookList from "./components/BookList"; +import AuthorList from "./components/AuthorList"; const App: Component = () => { - const [books, setBooks] = createSignal([]); - onMount(() => { - CalibreService.getBooks().then(setBooks); - }); return (
-

- Edit src/App.tsx and save to reload. -

- - Learn Solid - +
-
- {(item) => } -
+
+ + + + + + +
); }; diff --git a/Frontend/src/components/AuthorCard.tsx b/Frontend/src/components/AuthorCard.tsx new file mode 100644 index 0000000..eaae640 --- /dev/null +++ b/Frontend/src/components/AuthorCard.tsx @@ -0,0 +1,27 @@ +import { Show, type Component } from "solid-js"; +import { author } from "../types/types"; +import { Card } from "solid-bootstrap"; + +const AuthorCard: Component<{ author: author }> = (props: { + author: author; +}) => { + return ( + + + + {props.author.name} + + + {props.author.bookCount} books + + + + + ); +}; + +export default AuthorCard; diff --git a/Frontend/src/components/AuthorList.tsx b/Frontend/src/components/AuthorList.tsx new file mode 100644 index 0000000..b41b537 --- /dev/null +++ b/Frontend/src/components/AuthorList.tsx @@ -0,0 +1,22 @@ +import { createSignal, onMount, For, type Component } from "solid-js"; +import AuthorCard from "./AuthorCard"; +import BibblanService from "../services/bibblanservice"; +import { author } from "../types/types"; + +const BookList: Component = () => { + const [authors, setAuthors] = createSignal([]); + onMount(() => { + BibblanService.getAuthors().then(setAuthors); + }); + + return ( +
+

Books!

+
+ {(item) => } +
+
+ ); +}; + +export default BookList; diff --git a/Frontend/src/components/BookCard.tsx b/Frontend/src/components/BookCard.tsx index 7056f42..3e597f8 100644 --- a/Frontend/src/components/BookCard.tsx +++ b/Frontend/src/components/BookCard.tsx @@ -1,5 +1,5 @@ import { Show, type Component } from "solid-js"; -import { book } from "../types/calibretypes"; +import { book } from "../types/types"; import { Card } from "solid-bootstrap"; const BookCard: Component<{ book: book }> = (props: { book: book }) => { diff --git a/Frontend/src/components/BookList.tsx b/Frontend/src/components/BookList.tsx new file mode 100644 index 0000000..2992183 --- /dev/null +++ b/Frontend/src/components/BookList.tsx @@ -0,0 +1,24 @@ +import { createSignal, onMount, For, type Component } from "solid-js"; +import { useParams } from "@solidjs/router"; +import BookCard from "./BookCard"; +import BibblanService from "../services/bibblanservice"; +import { book } from "../types/types"; + +const BookList: Component = () => { + const [books, setBooks] = createSignal([]); + const params = useParams(); + onMount(() => { + BibblanService.getBooks(params.authorid).then(setBooks); + }); + + return ( +
+

Books!

+
+ {(item) => } +
+
+ ); +}; + +export default BookList; diff --git a/Frontend/src/components/Home.tsx b/Frontend/src/components/Home.tsx new file mode 100644 index 0000000..4e19da8 --- /dev/null +++ b/Frontend/src/components/Home.tsx @@ -0,0 +1,6 @@ +import { type Component } from "solid-js"; +const Home: Component = () => { + return

Home!

; +}; + +export default Home; diff --git a/Frontend/src/services/bibblanservice.ts b/Frontend/src/services/bibblanservice.ts new file mode 100644 index 0000000..7548925 --- /dev/null +++ b/Frontend/src/services/bibblanservice.ts @@ -0,0 +1,21 @@ +import { book, author } from "../types/types"; + +const BibblanService = { + getBooks: async ( + authorid: string | undefined = undefined + ): Promise => { + let url = "/api/bibblan/books"; + if (authorid != undefined) { + url += `/author/${authorid}`; + } + const response = await fetch(url); + return response.json(); + }, + + getAuthors: async (): Promise => { + const response = await fetch("/api/bibblan/authors"); + return response.json(); + }, +}; + +export default BibblanService; diff --git a/Frontend/src/services/calibreservice.ts b/Frontend/src/services/calibreservice.ts index 953284b..509e2f9 100644 --- a/Frontend/src/services/calibreservice.ts +++ b/Frontend/src/services/calibreservice.ts @@ -1,4 +1,4 @@ -import { book } from "../types/calibretypes"; +import { book } from "../types/types"; const CalibreService = { getBooks: async (): Promise => { diff --git a/Frontend/src/types/calibretypes.ts b/Frontend/src/types/types.ts similarity index 72% rename from Frontend/src/types/calibretypes.ts rename to Frontend/src/types/types.ts index 44beb1d..a6bf830 100644 --- a/Frontend/src/types/calibretypes.ts +++ b/Frontend/src/types/types.ts @@ -12,4 +12,10 @@ interface book { seriesNumber: number; } -export type { book }; +interface author { + id: number; + name: string; + bookCount: number; +} + +export type { book, author }; diff --git a/Frontend/yarn.lock b/Frontend/yarn.lock index 56d5c3f..42f3596 100644 --- a/Frontend/yarn.lock +++ b/Frontend/yarn.lock @@ -541,6 +541,11 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz#d81efe6a12060c7feddf9805e2a94c3ab0679f48" integrity sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg== +"@solidjs/router@^0.15.3": + version "0.15.3" + resolved "https://registry.yarnpkg.com/@solidjs/router/-/router-0.15.3.tgz#2c5e7aa637980ab7fce956aedc8cd20614163f2a" + integrity sha512-iEbW8UKok2Oio7o6Y4VTzLj+KFCmQPGEpm1fS3xixwFBdclFVBvaQVeibl1jys4cujfAK5Kn6+uG2uBm3lxOMw== + "@types/babel__core@^7.20.4": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" diff --git a/Server/Bibblan.csproj b/Server/Bibblan.csproj index 43fae32..5f08839 100644 --- a/Server/Bibblan.csproj +++ b/Server/Bibblan.csproj @@ -10,6 +10,7 @@ + 9.*-* diff --git a/Server/Business/Services/DatabaseService.cs b/Server/Business/Services/DatabaseService.cs new file mode 100644 index 0000000..51e8b0d --- /dev/null +++ b/Server/Business/Services/DatabaseService.cs @@ -0,0 +1,45 @@ +using Bibblan.Models; +using Bibblan.ViewModels; +using Dapper; +using Microsoft.Extensions.Options; +using Npgsql; + +namespace Bibblan.Business.Services +{ + + public class BookFilter + { + public int? Author; + } + + public class DatabaseService + { + BibblanOptions settings; + public DatabaseService(IOptions options) { + 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) + { + query += " where "; + if(filter.Author != null) + { + query += $"id in (select book from books_authors_link where author = {filter.Author})"; + } + } + return conn.Query(query).Take(count).ToList(); + } + + public IEnumerable GetAuthors(int count) + { + 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 "; + var conn = new NpgsqlConnection(settings.BibblanConnection); + return conn.Query(query).Take(count).ToList(); + + } + } +} diff --git a/Server/Controllers/BibblanController.cs b/Server/Controllers/BibblanController.cs new file mode 100644 index 0000000..77d7a8e --- /dev/null +++ b/Server/Controllers/BibblanController.cs @@ -0,0 +1,53 @@ +using Bibblan.Business.Services; +using Microsoft.AspNetCore.Mvc; + +namespace Bibblan.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class BibblanController : ControllerBase + { + DatabaseService _db; + CalibreService _calibre; + + public BibblanController(DatabaseService databaseService, CalibreService calibre) + { + _db = databaseService; + _calibre = calibre; + } + + [HttpGet("cover")] + public IActionResult GetCover(string path) + { + //TODO: Bör kanske inte gå direkt mot calibres filer.. + var bytes = _calibre.Cover(path); + return File(bytes, "image/jpeg", true); + } + + [HttpGet("books")] + public IActionResult GetBooks() + { + var authors = _db.GetBooks(100).ToList(); + return Ok(authors); + } + + [HttpGet("authors")] + public IActionResult GetAuthors() + { + var authors = _db.GetAuthors(100).ToList(); + return Ok(authors); + } + + [HttpGet("books/author/{authorid}")] + public IActionResult GetBooksByAuthor(int authorid) + { + var authors = _db.GetBooks(100, new BookFilter + { + Author = authorid + }).ToList(); + return Ok(authors); + } + + + } +} diff --git a/Server/Program.cs b/Server/Program.cs index a782f2a..1497e76 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -13,6 +13,7 @@ configSection.Bind(config); builder.Services.Configure(configSection); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddControllers(); @@ -26,6 +27,8 @@ builder.Services.AddDbContext(options => options.UseNpgs var app = builder.Build(); +Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; + app.UseDefaultFiles(); app.MapStaticAssets();