4 Commits
0.5.0 ... 0.6.0

Author SHA1 Message Date
b8eacb9c10 Release 0.6.0 2026-03-27 22:17:02 +01:00
e05c9f2b45 Use the same widget for books everywhere 2026-03-27 22:08:24 +01:00
726c640657 Change tab order 2026-03-27 21:30:42 +01:00
7b5da2df61 Improve book list buttons in mobile view 2026-03-27 14:16:59 +01:00
21 changed files with 107 additions and 169 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "bibliomane", "name": "bibliomane",
"version": "0.5.0", "version": "0.6.0",
"private": true, "private": true,
"type": "module", "type": "module",
"engines": { "engines": {

View File

@@ -83,12 +83,12 @@ onMounted(() => {
<div id="navmenu" class="navbar-menu" :class="isMenuActive ? 'is-active' : ''"> <div id="navmenu" class="navbar-menu" :class="isMenuActive ? 'is-active' : ''">
<div class="navbar-start"> <div class="navbar-start">
<NavBarSearch size-class="" class="is-hidden-touch" /> <NavBarSearch size-class="" class="is-hidden-touch" />
<RouterLink v-if="authStore.user" to="/browse" class="navbar-item" activeClass="is-active">
{{ $t('navbar.explore') }}
</RouterLink>
<RouterLink v-if="authStore.user" to="/books" class="navbar-item" activeClass="is-active"> <RouterLink v-if="authStore.user" to="/books" class="navbar-item" activeClass="is-active">
{{ $t('navbar.mybooks') }} {{ $t('navbar.mybooks') }}
</RouterLink> </RouterLink>
<RouterLink v-if="authStore.user" to="/browse" class="navbar-item" activeClass="is-active">
{{ $t('navbar.explore') }}
</RouterLink>
<RouterLink v-if="authStore.user" to="/add" class="navbar-item" activeClass="is-active"> <RouterLink v-if="authStore.user" to="/add" class="navbar-item" activeClass="is-active">
{{ $t('navbar.addbook') }} {{ $t('navbar.addbook') }}
</RouterLink> </RouterLink>

View File

@@ -1,67 +0,0 @@
<script setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { getImagePathOrDefault } from './api.js'
import { VRating } from 'vuetify/components/VRating'
const props = defineProps({
id: Number,
title: String,
author: String,
coverPath: String,
rating: Number,
read: Boolean,
})
const imagePathOrDefault = computed(() => getImagePathOrDefault(props.coverPath))
const router = useRouter()
function openBook() {
router.push(`/book/${props.id}`)
}
</script>
<template>
<div class="box container has-background-dark">
<div class="media" @click="openBook">
<div class="media-left">
<figure class="image mb-3">
<img v-bind:src="imagePathOrDefault" v-bind:alt="title" />
</figure>
</div>
<div class="media-content">
<div class="content">
<div class="is-size-5">{{ title }}</div>
<div class="is-size-5 is-italic">{{ author }}</div>
<VRating
v-if="rating > 0"
half-increments
readonly
:length="5"
size="medium"
:model-value="rating / 2"
active-color="bulma-body-color"
/>
</div>
</div>
</div>
</div>
</template>
<style scoped>
img {
max-height: 100px;
max-width: 100px;
height: auto;
width: auto;
}
.box {
transition: ease-in-out 0.04s;
margin-bottom: 15px;
}
.box:hover {
transform: scale(1.01);
transition: ease-in-out 0.02s;
}
</style>

View File

@@ -143,24 +143,26 @@ async function importInventaireEdition(inventaireid) {
</div> </div>
</div> </div>
<div v-if="id && id != 0" class="column is-narrow"> <div v-if="id && id != 0" class="column is-narrow">
<button @click="onUserBookWantRead" class="button is-large verticalbutton"> <div class="buttons">
<span class="icon" :title="$t('booklistelement.wantread')"> <button @click="onUserBookWantRead" class="button is-large verticalbutton">
<b-icon-eye-fill v-if="isWantRead" /> <span class="icon" :title="$t('booklistelement.wantread')">
<b-icon-eye v-else /> <b-icon-eye-fill v-if="isWantRead" />
</span> <b-icon-eye v-else />
</button> </span>
<button @click="onUserBookStartRead" class="button is-large verticalbutton"> </button>
<span class="icon" :title="$t('booklistelement.startread')"> <button @click="onUserBookStartRead" class="button is-large verticalbutton">
<b-icon-book-fill v-if="isStartRead" /> <span class="icon" :title="$t('booklistelement.startread')">
<b-icon-book v-else /> <b-icon-book-fill v-if="isStartRead" />
</span> <b-icon-book v-else />
</button> </span>
<button @click="onUserBookRead" class="button is-large verticalbutton"> </button>
<span class="icon" :title="$t('booklistelement.read')"> <button @click="onUserBookRead" class="button is-large verticalbutton">
<b-icon-check-circle-fill v-if="isRead" /> <span class="icon" :title="$t('booklistelement.read')">
<b-icon-check-circle v-else /> <b-icon-check-circle-fill v-if="isRead" />
</span> <b-icon-check-circle v-else />
</button> </span>
</button>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -183,6 +185,10 @@ img {
transition: ease-in-out 0.02s; transition: ease-in-out 0.02s;
} }
.buttons {
display: block;
}
.verticalbutton { .verticalbutton {
display: block; display: block;
} }
@@ -194,4 +200,12 @@ img {
.no-margin { .no-margin {
margin: 0px; margin: 0px;
} }
@media (max-width: 1024px) {
.buttons {
display: flex;
justify-content: center;
align-items: center;
}
}
</style> </style>

View File

@@ -1,7 +1,7 @@
<script setup> <script setup>
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import BookCard from './BookCard.vue'
import { getMyBooks } from './api.js' import { getMyBooks } from './api.js'
import BookListElement from './BookListElement.vue'
import Pagination from './Pagination.vue' import Pagination from './Pagination.vue'
const FilterStates = Object.freeze({ const FilterStates = Object.freeze({
@@ -10,7 +10,7 @@ const FilterStates = Object.freeze({
READING: 'reading', READING: 'reading',
}) })
const limit = 6 const limit = 5
const pageNumber = ref(1) const pageNumber = ref(1)
const offset = computed(() => (pageNumber.value - 1) * limit) const offset = computed(() => (pageNumber.value - 1) * limit)
@@ -76,7 +76,7 @@ function pageChange(newPageNumber) {
<div v-else-if="data"> <div v-else-if="data">
<div class=""> <div class="">
<div class="" v-for="book in data.books" :key="book.id"> <div class="" v-for="book in data.books" :key="book.id">
<BookCard v-bind="book" /> <BookListElement v-bind="book" />
</div> </div>
</div> </div>
<Pagination <Pagination

View File

@@ -19,7 +19,7 @@ func TestFetchAllBooks(t *testing.T) {
assert.Equal(t, 15, len(result.Books)) assert.Equal(t, 15, len(result.Books))
} }
func testFetchBooks(t *testing.T, limit string, offset string) dto.BookSearchGet { func testFetchBooks(t *testing.T, limit string, offset string) dto.BookItemsGet {
router := testutils.TestSetup() router := testutils.TestSetup()
u, err := url.Parse("/ws/books") u, err := url.Parse("/ws/books")
@@ -47,7 +47,7 @@ func testFetchBooks(t *testing.T, limit string, offset string) dto.BookSearchGet
w := httptest.NewRecorder() w := httptest.NewRecorder()
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
var result dto.BookSearchGet var result dto.BookItemsGet
s := w.Body.String() s := w.Body.String()
err = json.Unmarshal([]byte(s), &result) err = json.Unmarshal([]byte(s), &result)
if err != nil { if err != nil {

View File

@@ -15,7 +15,7 @@ import (
func TestGetBook_Ok(t *testing.T) { func TestGetBook_Ok(t *testing.T) {
book := testGetBook(t, "3", http.StatusOK) book := testGetBook(t, "3", http.StatusOK)
assert.Equal(t, assert.Equal(t,
dto.BookGet{ dto.FullBookGet{
Title: "D'un château l'autre", Title: "D'un château l'autre",
Author: "Louis-Ferdinand Céline", Author: "Louis-Ferdinand Céline",
AuthorID: 2, AuthorID: 2,
@@ -29,7 +29,7 @@ func TestGetBook_Ok(t *testing.T) {
func TestGetBook_NoUserBook(t *testing.T) { func TestGetBook_NoUserBook(t *testing.T) {
book := testGetBook(t, "18", http.StatusOK) book := testGetBook(t, "18", http.StatusOK)
assert.Equal(t, assert.Equal(t,
dto.BookGet{ dto.FullBookGet{
Title: "De sang-froid", Title: "De sang-froid",
Author: "Truman Capote", Author: "Truman Capote",
AuthorID: 14, AuthorID: 14,
@@ -41,7 +41,7 @@ func TestGetBook_NoUserBook(t *testing.T) {
func TestGetBook_Description(t *testing.T) { func TestGetBook_Description(t *testing.T) {
book := testGetBook(t, "22", http.StatusOK) book := testGetBook(t, "22", http.StatusOK)
assert.Equal(t, assert.Equal(t,
dto.BookGet{ dto.FullBookGet{
Title: "Le complot contre l'Amérique", Title: "Le complot contre l'Amérique",
Author: "Philip Roth", Author: "Philip Roth",
AuthorID: 17, AuthorID: 17,
@@ -61,7 +61,7 @@ func TestGetBook_IdNotInt(t *testing.T) {
testGetBook(t, "wrong", http.StatusBadRequest) testGetBook(t, "wrong", http.StatusBadRequest)
} }
func testGetBook(t *testing.T, id string, status int) dto.BookGet { func testGetBook(t *testing.T, id string, status int) dto.FullBookGet {
router := testutils.TestSetup() router := testutils.TestSetup()
token := testutils.ConnectDemoUser(router) token := testutils.ConnectDemoUser(router)
@@ -70,7 +70,7 @@ func testGetBook(t *testing.T, id string, status int) dto.BookGet {
w := httptest.NewRecorder() w := httptest.NewRecorder()
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
var book dto.BookGet var book dto.FullBookGet
err := json.Unmarshal(w.Body.Bytes(), &book) err := json.Unmarshal(w.Body.Bytes(), &book)
if err != nil { if err != nil {
t.Error(err) t.Error(err)

View File

@@ -60,14 +60,14 @@ func TestGetReadBooksHandler_CheckOneBook(t *testing.T) {
token := testutils.ConnectDemo2User(router) token := testutils.ConnectDemo2User(router)
result := testGetReadBooksHandler(t, router, token, 200, "100", "") result := testGetReadBooksHandler(t, router, token, 200, "100", "")
var book dto.BookUserGetBook var book dto.BookItemGet
for _, b := range result.Books { for _, b := range result.Books {
if b.Title == "De sang-froid" { if b.Title == "De sang-froid" {
book = b book = b
} }
} }
assert.Equal(t, assert.Equal(t,
dto.BookUserGetBook{ dto.BookItemGet{
ID: 18, ID: 18,
Title: "De sang-froid", Title: "De sang-froid",
Author: "Truman Capote", Author: "Truman Capote",
@@ -77,7 +77,7 @@ func TestGetReadBooksHandler_CheckOneBook(t *testing.T) {
}, book) }, book)
} }
func testGetReadBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, limit string, offset string) dto.BookUserGet { func testGetReadBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, limit string, offset string) dto.BookItemsGet {
u, err := url.Parse("/ws/mybooks/read") u, err := url.Parse("/ws/mybooks/read")
if err != nil { if err != nil {
t.Error(err) t.Error(err)

View File

@@ -28,7 +28,7 @@ func TestGetReadingBooksHandler_Demo2(t *testing.T) {
assert.Equal(t, int64(0), result.Count) assert.Equal(t, int64(0), result.Count)
} }
func testGetReadingBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, limit string, offset string) dto.BookUserGet { func testGetReadingBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, limit string, offset string) dto.BookItemsGet {
u, err := url.Parse("/ws/mybooks/reading") u, err := url.Parse("/ws/mybooks/reading")
if err != nil { if err != nil {
t.Error(err) t.Error(err)

View File

@@ -13,14 +13,14 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func testGetbooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, url string) dto.BookUserGet { func testGetbooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int, url string) dto.BookItemsGet {
req, _ := http.NewRequest("GET", url, nil) req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", userToken)) req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", userToken))
w := httptest.NewRecorder() w := httptest.NewRecorder()
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
var parsedResponse dto.BookUserGet var parsedResponse dto.BookItemsGet
err := json.Unmarshal(w.Body.Bytes(), &parsedResponse) err := json.Unmarshal(w.Body.Bytes(), &parsedResponse)
if err != nil { if err != nil {
t.Error(err) t.Error(err)

View File

@@ -27,6 +27,6 @@ func TestGetWantReadBooksHandler_Demo2(t *testing.T) {
assert.Equal(t, int64(0), result.Count) assert.Equal(t, int64(0), result.Count)
} }
func testGetWantReadBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int) dto.BookUserGet { func testGetWantReadBooksHandler(t *testing.T, router *gin.Engine, userToken string, expectedCode int) dto.BookItemsGet {
return testGetbooksHandler(t, router, userToken, expectedCode, "/ws/mybooks/wantread") return testGetbooksHandler(t, router, userToken, expectedCode, "/ws/mybooks/wantread")
} }

View File

@@ -25,7 +25,7 @@ func TestSearchBook_OneBookNotUserBook(t *testing.T) {
result := testSearchBook(t, "iliade", "", "") result := testSearchBook(t, "iliade", "", "")
assert.Equal(t, int64(1), result.Count) assert.Equal(t, int64(1), result.Count)
assert.Equal(t, assert.Equal(t,
[]dto.BookSearchGetBook{{ []dto.BookItemGet{{
Title: "Iliade", Title: "Iliade",
Author: "Homère", Author: "Homère",
ID: 29, ID: 29,
@@ -41,7 +41,7 @@ func TestSearchBook_OneBookRead(t *testing.T) {
result := testSearchBook(t, "dieux", "", "") result := testSearchBook(t, "dieux", "", "")
assert.Equal(t, int64(1), result.Count) assert.Equal(t, int64(1), result.Count)
assert.Equal(t, assert.Equal(t,
[]dto.BookSearchGetBook{{ []dto.BookItemGet{{
Title: "Les dieux ont soif", Title: "Les dieux ont soif",
Author: "Anatole France", Author: "Anatole France",
ID: 4, ID: 4,
@@ -58,7 +58,7 @@ func TestSearchBook_OneBookStartRead(t *testing.T) {
result := testSearchBook(t, "Recherches", "", "") result := testSearchBook(t, "Recherches", "", "")
assert.Equal(t, int64(1), result.Count) assert.Equal(t, int64(1), result.Count)
assert.Equal(t, assert.Equal(t,
[]dto.BookSearchGetBook{{ []dto.BookItemGet{{
Title: "Recherches philosophiques", Title: "Recherches philosophiques",
Author: "Ludwig Wittgenstein", Author: "Ludwig Wittgenstein",
ID: 30, ID: 30,
@@ -75,7 +75,7 @@ func TestSearchBook_ISBN(t *testing.T) {
result := testSearchBook(t, "9782070337903", "", "") result := testSearchBook(t, "9782070337903", "", "")
assert.Equal(t, int64(1), result.Count) assert.Equal(t, int64(1), result.Count)
assert.Equal(t, assert.Equal(t,
[]dto.BookSearchGetBook{{ []dto.BookItemGet{{
Title: "Le complot contre l'Amérique", Title: "Le complot contre l'Amérique",
Author: "Philip Roth", Author: "Philip Roth",
ID: 22, ID: 22,
@@ -91,7 +91,7 @@ func TestSearchBook_ISBNInventaire(t *testing.T) {
result := testSearchBook(t, "9782253158400", "", "") result := testSearchBook(t, "9782253158400", "", "")
assert.Equal(t, int64(1), result.Count) assert.Equal(t, int64(1), result.Count)
assert.Equal(t, assert.Equal(t,
[]dto.BookSearchGetBook{{ []dto.BookItemGet{{
ID: 0, ID: 0,
Title: "Les premières enquêtes de Maigret", Title: "Les premières enquêtes de Maigret",
Author: "Georges Simenon", Author: "Georges Simenon",
@@ -117,7 +117,7 @@ func TestSearchBook_Offset(t *testing.T) {
assert.Equal(t, 3, len(result.Books)) assert.Equal(t, 3, len(result.Books))
} }
func testSearchBook(t *testing.T, searchterm string, limit string, offset string) dto.BookSearchGet { func testSearchBook(t *testing.T, searchterm string, limit string, offset string) dto.BookItemsGet {
router := testutils.TestSetup() router := testutils.TestSetup()
u, err := url.Parse("/ws/search/" + searchterm) u, err := url.Parse("/ws/search/" + searchterm)
@@ -145,7 +145,7 @@ func testSearchBook(t *testing.T, searchterm string, limit string, offset string
w := httptest.NewRecorder() w := httptest.NewRecorder()
router.ServeHTTP(w, req) router.ServeHTTP(w, req)
var result dto.BookSearchGet var result dto.BookItemsGet
s := w.Body.String() s := w.Body.String()
err = json.Unmarshal([]byte(s), &result) err = json.Unmarshal([]byte(s), &result)
if err != nil { if err != nil {

View File

@@ -6,7 +6,7 @@ type AppInfo struct {
DemoUsername string `json:"demoUsername"` DemoUsername string `json:"demoUsername"`
} }
type BookGet struct { type FullBookGet struct {
Title string `json:"title" binding:"required,max=300"` Title string `json:"title" binding:"required,max=300"`
Author string `json:"author" binding:"max=100"` Author string `json:"author" binding:"max=100"`
AuthorID uint `json:"authorId"` AuthorID uint `json:"authorId"`
@@ -23,28 +23,13 @@ type BookGet struct {
CoverPath string `json:"coverPath"` CoverPath string `json:"coverPath"`
} }
type BookUserGet struct { type BookItemsGet struct {
Count int64 `json:"count"` Count int64 `json:"count"`
Books []BookUserGetBook `json:"books"` Inventaire bool `json:"inventaire"`
Books []BookItemGet `json:"books"`
} }
type BookUserGetBook struct { type BookItemGet struct {
ID uint `json:"id"`
Title string `json:"title" binding:"required,max=300"`
Author string `json:"author" binding:"max=100"`
Rating int `json:"rating" binding:"min=0,max=10"`
Read bool `json:"read" binding:"boolean"`
WantRead bool `json:"wantread" binding:"boolean"`
CoverPath string `json:"coverPath"`
}
type BookSearchGet struct {
Count int64 `json:"count"`
Inventaire bool `json:"inventaire"`
Books []BookSearchGetBook `json:"books"`
}
type BookSearchGetBook struct {
ID uint `json:"id"` ID uint `json:"id"`
Title string `json:"title" binding:"required,max=300"` Title string `json:"title" binding:"required,max=300"`
Author string `json:"author" binding:"max=100"` Author string `json:"author" binding:"max=100"`

View File

@@ -10,8 +10,8 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
func FetchBookGet(db *gorm.DB, userId uint, bookId uint64) (dto.BookGet, error) { func FetchBookGet(db *gorm.DB, userId uint, bookId uint64) (dto.FullBookGet, error) {
var book dto.BookGet var book dto.FullBookGet
query := db.Model(&model.Book{}) query := db.Model(&model.Book{})
selectQueryString := "books.title, authors.name as author, authors.id as author_id, books.isbn, books.inventaire_id, books.open_library_id, books.summary, " + selectQueryString := "books.title, authors.name as author, authors.id as author_id, books.isbn, books.inventaire_id, books.open_library_id, books.summary, " +
"user_books.review, user_books.rating, user_books.read, user_books.want_read, " + "user_books.review, user_books.rating, user_books.read, user_books.want_read, " +
@@ -27,8 +27,8 @@ func FetchBookGet(db *gorm.DB, userId uint, bookId uint64) (dto.BookGet, error)
return book, res.Error return book, res.Error
} }
func FetchReadUserBook(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookUserGetBook, error) { func FetchReadUserBook(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookItemGet, error) {
var books []dto.BookUserGetBook var books []dto.BookItemGet
query := fetchReadUserBookQuery(db, userId) query := fetchReadUserBookQuery(db, userId)
query = query.Limit(limit) query = query.Limit(limit)
query = query.Offset(offset) query = query.Offset(offset)
@@ -43,8 +43,8 @@ func FetchReadUserBookCount(db *gorm.DB, userId uint) (int64, error) {
return count, res.Error return count, res.Error
} }
func FetchReadingUserBook(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookUserGetBook, error) { func FetchReadingUserBook(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookItemGet, error) {
var books []dto.BookUserGetBook var books []dto.BookItemGet
query := fetchReadingUserBookQuery(db, userId) query := fetchReadingUserBookQuery(db, userId)
query = query.Limit(limit) query = query.Limit(limit)
query = query.Offset(offset) query = query.Offset(offset)
@@ -70,8 +70,8 @@ func fetchReadingUserBookQuery(db *gorm.DB, userId uint) *gorm.DB {
return query return query
} }
func FetchWantReadUserBook(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookUserGetBook, error) { func FetchWantReadUserBook(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookItemGet, error) {
var books []dto.BookUserGetBook var books []dto.BookItemGet
query := fetchWantReadUserBookQuery(db, userId) query := fetchWantReadUserBookQuery(db, userId)
query = query.Limit(limit) query = query.Limit(limit)
@@ -93,9 +93,10 @@ func fetchWantReadUserBookQuery(db *gorm.DB, userId uint) *gorm.DB {
return query return query
} }
// fetch only books where userbook exists
func fetchUserBookGet(db *gorm.DB, userId uint) *gorm.DB { func fetchUserBookGet(db *gorm.DB, userId uint) *gorm.DB {
query := db.Model(&model.UserBook{}) query := db.Model(&model.UserBook{})
query = query.Select("books.id, books.title, authors.name as author, user_books.rating, user_books.read, user_books.want_read, " + selectStaticFilesPath()) query = query.Select(selectBookItem())
query = query.Joins("left join books on (books.id = user_books.book_id)") query = query.Joins("left join books on (books.id = user_books.book_id)")
query = joinAuthors(query) query = joinAuthors(query)
query = joinStaticFiles(query) query = joinStaticFiles(query)
@@ -103,8 +104,8 @@ func fetchUserBookGet(db *gorm.DB, userId uint) *gorm.DB {
return query return query
} }
func FetchAllBooks(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookSearchGetBook, error) { func FetchAllBooks(db *gorm.DB, userId uint, limit int, offset int) ([]dto.BookItemGet, error) {
var books []dto.BookSearchGetBook var books []dto.BookItemGet
query := fetchBookQueryBuilder(db, userId) query := fetchBookQueryBuilder(db, userId)
query = query.Limit(limit) query = query.Limit(limit)
query = query.Offset(offset) query = query.Offset(offset)
@@ -120,8 +121,8 @@ func FetchAllBooksCount(db *gorm.DB, userId uint) (int64, error) {
return count, res.Error return count, res.Error
} }
func FetchBookSearchByAuthorGet(db *gorm.DB, userId uint, authorId uint64, limit int, offset int) ([]dto.BookSearchGetBook, error) { func FetchBookSearchByAuthorGet(db *gorm.DB, userId uint, authorId uint64, limit int, offset int) ([]dto.BookItemGet, error) {
var books []dto.BookSearchGetBook var books []dto.BookItemGet
query := fetchBookSearchByAuthorQuery(db, userId, authorId) query := fetchBookSearchByAuthorQuery(db, userId, authorId)
query = query.Limit(limit) query = query.Limit(limit)
query = query.Offset(offset) query = query.Offset(offset)
@@ -141,8 +142,8 @@ func fetchBookSearchByAuthorQuery(db *gorm.DB, userId uint, authorId uint64) *go
return query.Where("authors.id = ?", authorId) return query.Where("authors.id = ?", authorId)
} }
func FetchBookSearchGet(db *gorm.DB, userId uint, searchterm string, limit int, offset int) ([]dto.BookSearchGetBook, error) { func FetchBookSearchGet(db *gorm.DB, userId uint, searchterm string, limit int, offset int) ([]dto.BookItemGet, error) {
var books []dto.BookSearchGetBook var books []dto.BookItemGet
query := fetchBookSearchQuery(db, userId, searchterm) query := fetchBookSearchQuery(db, userId, searchterm)
query = query.Limit(limit) query = query.Limit(limit)
query = query.Offset(offset) query = query.Offset(offset)
@@ -169,15 +170,20 @@ func fetchBookSearchQuery(db *gorm.DB, userId uint, searchterm string) *gorm.DB
return query return query
} }
// fetch all books even whithout user books
func fetchBookQueryBuilder(db *gorm.DB, userId uint) *gorm.DB { func fetchBookQueryBuilder(db *gorm.DB, userId uint) *gorm.DB {
query := db.Model(&model.Book{}) query := db.Model(&model.Book{})
query = query.Select("books.id, books.title, authors.name as author, books.small_description as description, books.inventaire_id, user_books.rating, user_books.read, DATE(user_books.start_read_date) as start_read_date, user_books.want_read, " + selectStaticFilesPath()) query = query.Select(selectBookItem())
query = joinAuthors(query) query = joinAuthors(query)
query = query.Joins("left join user_books on (user_books.book_id = books.id and user_books.user_id = ?)", userId) query = query.Joins("left join user_books on (user_books.book_id = books.id and user_books.user_id = ?)", userId)
query = joinStaticFiles(query) query = joinStaticFiles(query)
return query return query
} }
func selectBookItem() string {
return "books.id, books.title, authors.name as author, books.small_description as description, books.inventaire_id, user_books.rating, user_books.read, DATE(user_books.start_read_date) as start_read_date, user_books.want_read, " + selectStaticFilesPath()
}
func selectStaticFilesPath() string { func selectStaticFilesPath() string {
return "(CASE COALESCE(static_files.path, '') WHEN '' THEN '' ELSE concat('" + fileutils.GetWsLinkPrefix() + "', static_files.path) END) as CoverPath" return "(CASE COALESCE(static_files.path, '') WHEN '' THEN '' ELSE concat('" + fileutils.GetWsLinkPrefix() + "', static_files.path) END) as CoverPath"
} }

View File

@@ -50,5 +50,5 @@ func GetAuthorBooksHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
ac.C.JSON(http.StatusOK, dto.BookSearchGet{Books: books, Count: count}) ac.C.JSON(http.StatusOK, dto.BookItemsGet{Books: books, Count: count})
} }

View File

@@ -37,7 +37,7 @@ func GetSearchBooksHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
var returnedBooks dto.BookSearchGet var returnedBooks dto.BookItemsGet
if !params.Inventaire { if !params.Inventaire {
books, err := query.FetchBookSearchGet(ac.Db, user.ID, searchterm, limit, offset) books, err := query.FetchBookSearchGet(ac.Db, user.ID, searchterm, limit, offset)
if err != nil { if err != nil {
@@ -49,7 +49,7 @@ func GetSearchBooksHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
returnedBooks = dto.BookSearchGet{Count: count, Inventaire: false, Books: books} returnedBooks = dto.BookItemsGet{Count: count, Inventaire: false, Books: books}
} }
if params.Inventaire || len(returnedBooks.Books) == 0 { if params.Inventaire || len(returnedBooks.Books) == 0 {
returnedBooksPtr, err := searchInInventaireAPI(ac.Config.InventaireUrl, searchterm, limit, offset, params) returnedBooksPtr, err := searchInInventaireAPI(ac.Config.InventaireUrl, searchterm, limit, offset, params)
@@ -62,7 +62,7 @@ func GetSearchBooksHandler(ac appcontext.AppContext) {
ac.C.JSON(http.StatusOK, returnedBooks) ac.C.JSON(http.StatusOK, returnedBooks)
} }
func searchInInventaireAPI(inventaireUrl string, searchterm string, limit int, offset int, params dto.BookSearchGetParam) (*dto.BookSearchGet, error) { func searchInInventaireAPI(inventaireUrl string, searchterm string, limit int, offset int, params dto.BookSearchGetParam) (*dto.BookItemsGet, error) {
isIsbn, err := regexp.Match(`\d{10,13}`, []byte(searchterm)) isIsbn, err := regexp.Match(`\d{10,13}`, []byte(searchterm))
if err != nil { if err != nil {
@@ -74,11 +74,11 @@ func searchInInventaireAPI(inventaireUrl string, searchterm string, limit int, o
if err != nil { if err != nil {
return nil, err return nil, err
} }
var bookSearchGet dto.BookSearchGet var bookSearchGet dto.BookItemsGet
if queryResult != nil { if queryResult != nil {
bookSearchGet = inventaireEditionToBookSearchGet(*queryResult) bookSearchGet = inventaireEditionToBookSearchGet(*queryResult)
} else { } else {
bookSearchGet = dto.BookSearchGet{Count: 0, Inventaire: true} bookSearchGet = dto.BookItemsGet{Count: 0, Inventaire: true}
} }
return &bookSearchGet, err return &bookSearchGet, err
} else { } else {
@@ -91,9 +91,9 @@ func searchInInventaireAPI(inventaireUrl string, searchterm string, limit int, o
} }
} }
func inventaireEditionToBookSearchGet(result inventaire.InventaireEditionDetailedSingleResult) dto.BookSearchGet { func inventaireEditionToBookSearchGet(result inventaire.InventaireEditionDetailedSingleResult) dto.BookItemsGet {
var books []dto.BookSearchGetBook var books []dto.BookItemGet
bookSearchGetBook := dto.BookSearchGetBook{ bookSearchGetBook := dto.BookItemGet{
ID: 0, ID: 0,
Title: result.Title, Title: result.Title,
Author: result.Author.Name, Author: result.Author.Name,
@@ -106,17 +106,17 @@ func inventaireEditionToBookSearchGet(result inventaire.InventaireEditionDetaile
CoverPath: result.Image, CoverPath: result.Image,
} }
books = append(books, bookSearchGetBook) books = append(books, bookSearchGetBook)
return dto.BookSearchGet{Count: 1, Inventaire: true, Books: books} return dto.BookItemsGet{Count: 1, Inventaire: true, Books: books}
} }
func inventaireBooksToBookSearchGet(inventaireUrl string, results inventaire.InventaireSearchResult) dto.BookSearchGet { func inventaireBooksToBookSearchGet(inventaireUrl string, results inventaire.InventaireSearchResult) dto.BookItemsGet {
var books []dto.BookSearchGetBook var books []dto.BookItemGet
for _, b := range results.Results { for _, b := range results.Results {
coverPath := "" coverPath := ""
if b.Image != "" && strings.HasPrefix(b.Image, "/") { if b.Image != "" && strings.HasPrefix(b.Image, "/") {
coverPath = inventaireUrl + b.Image coverPath = inventaireUrl + b.Image
} }
bookSearchGetBook := dto.BookSearchGetBook{ bookSearchGetBook := dto.BookItemGet{
ID: 0, ID: 0,
Title: b.Label, Title: b.Label,
Author: "", Author: "",
@@ -129,5 +129,5 @@ func inventaireBooksToBookSearchGet(inventaireUrl string, results inventaire.Inv
} }
books = append(books, bookSearchGetBook) books = append(books, bookSearchGetBook)
} }
return dto.BookSearchGet{Count: results.Total, Inventaire: true, Books: books} return dto.BookItemsGet{Count: results.Total, Inventaire: true, Books: books}
} }

View File

@@ -36,5 +36,5 @@ func GetBooksHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
ac.C.JSON(http.StatusOK, dto.BookSearchGet{Count: count, Inventaire: false, Books: books}) ac.C.JSON(http.StatusOK, dto.BookItemsGet{Count: count, Inventaire: false, Books: books})
} }

View File

@@ -35,5 +35,5 @@ func GetMyBooksReadHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
ac.C.JSON(http.StatusOK, dto.BookUserGet{Count: count, Books: userbooks}) ac.C.JSON(http.StatusOK, dto.BookItemsGet{Count: count, Books: userbooks})
} }

View File

@@ -35,5 +35,5 @@ func GetMyBooksReadingHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
ac.C.JSON(http.StatusOK, dto.BookUserGet{Count: count, Books: userbooks}) ac.C.JSON(http.StatusOK, dto.BookItemsGet{Count: count, Books: userbooks})
} }

View File

@@ -35,5 +35,5 @@ func GetMyBooksWantReadHandler(ac appcontext.AppContext) {
myvalidator.ReturnErrorsAsJsonResponse(&ac, err) myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
return return
} }
ac.C.JSON(http.StatusOK, dto.BookUserGet{Count: count, Books: userbooks}) ac.C.JSON(http.StatusOK, dto.BookItemsGet{Count: count, Books: userbooks})
} }

View File

@@ -6,7 +6,7 @@ import (
) )
func main() { func main() {
applicationVersion := "0.5.0" applicationVersion := "0.6.0"
c := config.LoadConfig(applicationVersion) c := config.LoadConfig(applicationVersion)
r := setup.Setup(&c) r := setup.Setup(&c)
r.Run(":" + c.Port) r.Run(":" + c.Port)