Books list: make the buttons work like in the form

This commit is contained in:
2026-03-26 17:07:22 +01:00
parent 4d687e3dcb
commit d8fc7396ff
6 changed files with 111 additions and 27 deletions

View File

@@ -6,6 +6,7 @@ import {
putUpdateBook,
putStartReadDate,
putStartReadDateUnset,
putReadBook,
putEndReadDate,
putEndReadDateUnset,
putUnreadBook,
@@ -49,7 +50,7 @@ async function onReadIconClick() {
if (data.value.read) {
data.value.wantread = false
data.value.endReadDate = today
putEndReadDate(props.id, today)
putReadBook(props.id)
} else {
putUnreadBook(props.id)
}

View File

@@ -1,6 +1,14 @@
<script setup>
import { ref, computed } from 'vue'
import { putReadBook, getImagePathOrDefault, postImportBook } from './api.js'
import {
putUpdateBook,
putReadBook,
putUnreadBook,
putStartRead,
putStartReadDateUnset,
getImagePathOrDefault,
postImportBook,
} from './api.js'
import { useRouter } from 'vue-router'
const router = useRouter()
@@ -14,17 +22,87 @@ const props = defineProps({
description: String,
rating: Number,
read: Boolean,
startread: Boolean,
startreaddate: String,
wantread: Boolean,
coverPath: String,
})
const imagePathOrDefault = computed(() => getImagePathOrDefault(props.coverPath))
const error = ref(null)
const isWantRead = ref(props.wantread)
const currentStartReadDate = ref(props.startreaddate)
const isRead = ref(props.read)
const today = new Date().toISOString().slice(0, 10)
const isStartRead = computed(
() => currentStartReadDate && currentStartReadDate.value && isRead && !isRead.value,
)
async function onUserBookRead() {
if (!isRead.value) {
userBookRead()
} else {
userBookUnread()
}
}
async function userBookRead() {
const res = await putReadBook(props.id)
if (res.ok) {
router.push('/books')
currentStartReadDate.value = ''
isWantRead.value = false
isRead.value = true
} else {
res.json().then((json) => (error.value = json))
}
}
async function userBookUnread() {
const res = await putUnreadBook(props.id)
if (res.ok) {
isRead.value = false
} else {
res.json().then((json) => (error.value = json))
}
}
async function onUserBookWantRead() {
const res = await putUpdateBook(props.id, { wantread: !isWantRead.value })
if (res.ok) {
isWantRead.value = !isWantRead.value
} else {
res.json().then((json) => (error.value = json))
}
}
async function onUserBookStartRead() {
if (!isStartRead.value) {
userBookStartRead()
} else {
userBookCancelStartRead()
}
}
async function userBookStartRead() {
const res = await putStartRead(props.id)
if (!res.ok) {
res.json().then((json) => (error.value = json))
return
}
const resp = await putUnreadBook(props.id)
if (resp.ok) {
currentStartReadDate.value = today
isRead.value = false
} else {
resp.json().then((json) => (error.value = json))
}
}
async function userBookCancelStartRead() {
const res = await putStartReadDateUnset(props.id)
if (res.ok) {
currentStartReadDate.value = ''
} else {
res.json().then((json) => (error.value = json))
}
@@ -69,21 +147,21 @@ async function importInventaireEdition(inventaireid) {
</div>
</div>
<div v-if="id && id != 0" class="column is-narrow">
<button @click="" class="button is-large verticalbutton">
<button @click="onUserBookWantRead" class="button is-large verticalbutton">
<span class="icon" :title="$t('booklistelement.wantread')">
<b-icon-eye-fill v-if="props.wantread" />
<b-icon-eye-fill v-if="isWantRead" />
<b-icon-eye v-else />
</span>
</button>
<button @click="" class="button is-large verticalbutton">
<button @click="onUserBookStartRead" class="button is-large verticalbutton">
<span class="icon" :title="$t('booklistelement.startread')">
<b-icon-book-fill v-if="props.startread" />
<b-icon-book-fill v-if="isStartRead" />
<b-icon-book v-else />
</span>
</button>
<button @click="onUserBookRead" class="button is-large verticalbutton">
<span class="icon" :title="$t('booklistelement.read')">
<b-icon-check-circle-fill v-if="props.read" />
<b-icon-check-circle-fill v-if="isRead" />
<b-icon-check-circle v-else />
</span>
</button>

View File

@@ -107,7 +107,7 @@ export async function postImportBook(id, language) {
}
export async function putReadBook(bookId) {
return genericPayloadCall('/ws/book/' + bookId, { read: true }, 'PUT')
return putEndReadDate(bookId, new Date().toISOString().slice(0, 10))
}
export async function putUnreadBook(bookId) {
@@ -126,6 +126,10 @@ export async function putStartReadDateUnset(bookId) {
return genericPayloadCall('/ws/book/' + bookId, { startDate: 'null' }, 'PUT')
}
export async function putStartRead(bookId) {
return putStartReadDate(bookId, new Date().toISOString().slice(0, 10))
}
export async function putStartReadDate(bookId, startdate) {
return genericPayloadCall('/ws/book/' + bookId, { startDate: startdate }, 'PUT')
}

View File

@@ -42,13 +42,14 @@ func TestSearchBook_OneBookRead(t *testing.T) {
assert.Equal(t, int64(1), result.Count)
assert.Equal(t,
[]dto.BookSearchGetBook{{
Title: "Les dieux ont soif",
Author: "Anatole France",
ID: 4,
Rating: 7,
Read: true,
WantRead: false,
CoverPath: "/static/bookcover/lesdieuxontsoif.jpg",
Title: "Les dieux ont soif",
Author: "Anatole France",
ID: 4,
Rating: 7,
Read: true,
StartReadDate: "2026-01-30",
WantRead: false,
CoverPath: "/static/bookcover/lesdieuxontsoif.jpg",
}},
result.Books)
}
@@ -58,14 +59,14 @@ func TestSearchBook_OneBookStartRead(t *testing.T) {
assert.Equal(t, int64(1), result.Count)
assert.Equal(t,
[]dto.BookSearchGetBook{{
Title: "Recherches philosophiques",
Author: "Ludwig Wittgenstein",
ID: 30,
Rating: 0,
Read: false,
StartRead: true,
WantRead: false,
CoverPath: "/static/bookcover/Recherches-philosophiques.jpg",
Title: "Recherches philosophiques",
Author: "Ludwig Wittgenstein",
ID: 30,
Rating: 0,
Read: false,
StartReadDate: "2025-11-22",
WantRead: false,
CoverPath: "/static/bookcover/Recherches-philosophiques.jpg",
}},
result.Books)
}

View File

@@ -53,7 +53,7 @@ type BookSearchGetBook struct {
IsInventaireEdition bool `json:"isinventaireedition"`
Rating int `json:"rating"`
Read bool `json:"read"`
StartRead bool `json:"startread"`
StartReadDate string `json:"startreaddate"`
WantRead bool `json:"wantread"`
CoverPath string `json:"coverPath"`
}

View File

@@ -171,7 +171,7 @@ func fetchBookSearchQuery(db *gorm.DB, userId uint, searchterm string) *gorm.DB
func fetchBookQueryBuilder(db *gorm.DB, userId uint) *gorm.DB {
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, (user_books.start_read_date IS NOT NULL AND (user_books.read IS NULL OR user_books.read IS FALSE)) as start_read, user_books.want_read, " + selectStaticFilesPath())
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 = joinAuthors(query)
query = query.Joins("left join user_books on (user_books.book_id = books.id and user_books.user_id = ?)", userId)
query = joinStaticFiles(query)