Add existing book fields in "create book" form
This commit is contained in:
@@ -9,6 +9,11 @@ const router = useRouter()
|
|||||||
const book = ref({
|
const book = ref({
|
||||||
title: '',
|
title: '',
|
||||||
author: '',
|
author: '',
|
||||||
|
isbn: '',
|
||||||
|
inventaireid: '',
|
||||||
|
openlibraryid: '',
|
||||||
|
shortdescription: '',
|
||||||
|
summary: '',
|
||||||
coverId: null,
|
coverId: null,
|
||||||
})
|
})
|
||||||
const errors = ref(null)
|
const errors = ref(null)
|
||||||
@@ -18,11 +23,26 @@ const titleError = computed(() => {
|
|||||||
const authorError = computed(() => {
|
const authorError = computed(() => {
|
||||||
return extractFormErrorFromField('Author', errors.value)
|
return extractFormErrorFromField('Author', errors.value)
|
||||||
})
|
})
|
||||||
|
const isbnError = computed(() => {
|
||||||
|
return extractFormErrorFromField('ISBN', errors.value)
|
||||||
|
})
|
||||||
|
const inventaireError = computed(() => {
|
||||||
|
return extractFormErrorFromField('InventaireID', errors.value)
|
||||||
|
})
|
||||||
|
const openLibraryError = computed(() => {
|
||||||
|
return extractFormErrorFromField('OpenLibraryId', errors.value)
|
||||||
|
})
|
||||||
|
const shortDescError = computed(() => {
|
||||||
|
return extractFormErrorFromField('ShortDescription', errors.value)
|
||||||
|
})
|
||||||
|
const summaryError = computed(() => {
|
||||||
|
return extractFormErrorFromField('ShortDescription', errors.value)
|
||||||
|
})
|
||||||
|
|
||||||
function onSubmit(e) {
|
function onSubmit(e) {
|
||||||
postBook(book).then((res) => {
|
postBook(book).then((res) => {
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
router.push('/')
|
res.json().then((json) => router.push('/book/' + json.id))
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
res.json().then((json) => (errors.value = json))
|
res.json().then((json) => (errors.value = json))
|
||||||
@@ -37,7 +57,7 @@ function onSubmit(e) {
|
|||||||
<label class="label">{{ $t('addbook.title') }}</label>
|
<label class="label">{{ $t('addbook.title') }}</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
:class="'input ' + (titleError ? 'is-danger' : '')"
|
:class="'input is-medium ' + (titleError ? 'is-danger' : '')"
|
||||||
type="text"
|
type="text"
|
||||||
maxlength="300"
|
maxlength="300"
|
||||||
required
|
required
|
||||||
@@ -51,7 +71,7 @@ function onSubmit(e) {
|
|||||||
<label class="label">{{ $t('addbook.author') }}</label>
|
<label class="label">{{ $t('addbook.author') }}</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input
|
<input
|
||||||
:class="'input ' + (authorError ? 'is-danger' : '')"
|
:class="'input is-medium ' + (authorError ? 'is-danger' : '')"
|
||||||
type="text"
|
type="text"
|
||||||
maxlength="100"
|
maxlength="100"
|
||||||
v-model="book.author"
|
v-model="book.author"
|
||||||
@@ -60,7 +80,71 @@ function onSubmit(e) {
|
|||||||
</div>
|
</div>
|
||||||
<p v-if="authorError" class="help is-danger">{{ authorError }}</p>
|
<p v-if="authorError" class="help is-danger">{{ authorError }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('addbook.shortdesc') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
:class="'input ' + (shortDescError ? 'is-danger' : '')"
|
||||||
|
type="text"
|
||||||
|
maxlength="300"
|
||||||
|
v-model="book.shortdescription"
|
||||||
|
:placeholder="$t('addbook.shortdesc')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p v-if="shortDescError" class="help is-danger">{{ shortDescError }}</p>
|
||||||
|
</div>
|
||||||
<CoverUpload name="cover" @on-image-upload="(id) => (book.coverId = id)" />
|
<CoverUpload name="cover" @on-image-upload="(id) => (book.coverId = id)" />
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">{{ $t('addbook.summary') }}</label>
|
||||||
|
<div class="control">
|
||||||
|
<textarea
|
||||||
|
:class="'textarea ' + (summaryError ? 'is-danger' : '')"
|
||||||
|
type="text"
|
||||||
|
v-model="book.summary"
|
||||||
|
:placeholder="$t('addbook.summary')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p v-if="summaryError" class="help is-danger">{{ summaryError }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">ISBN</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
:class="'input ' + (isbnError ? 'is-danger' : '')"
|
||||||
|
type="text"
|
||||||
|
maxlength="18"
|
||||||
|
v-model="book.isbn"
|
||||||
|
placeholder="ISBN"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p v-if="isbnError" class="help is-danger">{{ isbnError }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Inventaire</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
:class="'input ' + (inventaireError ? 'is-danger' : '')"
|
||||||
|
type="text"
|
||||||
|
maxlength="50"
|
||||||
|
v-model="book.inventaireid"
|
||||||
|
placeholder="Inventaire"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p v-if="inventaireError" class="help is-danger">{{ inventaireError }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">OpenLibrary</label>
|
||||||
|
<div class="control">
|
||||||
|
<input
|
||||||
|
:class="'input ' + (openLibraryError ? 'is-danger' : '')"
|
||||||
|
type="text"
|
||||||
|
maxlength="50"
|
||||||
|
v-model="book.openlibraryid"
|
||||||
|
placeholder="OpenLibrary"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p v-if="openLibraryError" class="help is-danger">{{ openLibraryError }}</p>
|
||||||
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-link">{{ $t('addbook.submit') }}</button>
|
<button class="button is-link">{{ $t('addbook.submit') }}</button>
|
||||||
|
|||||||
@@ -191,10 +191,7 @@ export function genericPayloadCall(apiRoute, object, method) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function extractFormErrorFromField(fieldName, errors) {
|
export function extractFormErrorFromField(fieldName, errors) {
|
||||||
if (errors === null) {
|
if (errors == null) {
|
||||||
return ''
|
|
||||||
}
|
|
||||||
if (errors.value == null) {
|
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
console.log(errors.value)
|
console.log(errors.value)
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
"addbook": {
|
"addbook": {
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"author": "Author",
|
"author": "Author",
|
||||||
|
"shortdesc": "Short description",
|
||||||
|
"summary": "Summary",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"coverupload": "Upload cover"
|
"coverupload": "Upload cover"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
"addbook": {
|
"addbook": {
|
||||||
"title": "Titre",
|
"title": "Titre",
|
||||||
"author": "Auteur",
|
"author": "Auteur",
|
||||||
|
"shortdesc": "Description rapide",
|
||||||
|
"summary": "Résumé",
|
||||||
"submit": "Confirmer",
|
"submit": "Confirmer",
|
||||||
"coverupload": "Téléverser la couverture"
|
"coverupload": "Téléverser la couverture"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package apitest
|
package apitest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.artlef.fr/bibliomane/internal/dto"
|
||||||
"git.artlef.fr/bibliomane/internal/testutils"
|
"git.artlef.fr/bibliomane/internal/testutils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -45,6 +48,36 @@ func TestPostBookHandler_noTitle(t *testing.T) {
|
|||||||
testPostBookHandler(t, bookJson, 400)
|
testPostBookHandler(t, bookJson, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPostBookHandler_AllFields(t *testing.T) {
|
||||||
|
bookJson :=
|
||||||
|
`{
|
||||||
|
"author": "Kafka",
|
||||||
|
"title": "Amerika",
|
||||||
|
"isbn": "978-2-07-036803-7",
|
||||||
|
"inventaireid": "isbn:9782070368037",
|
||||||
|
"openlibraryid": "OL8838048M",
|
||||||
|
"shortdescription": "Roman de Franz Kafka",
|
||||||
|
"summary": "L'Amérique (Amerika en version originale allemande) ou Le Disparu (Der Verschollene, titre voulu par l'auteur et rendu au livre dans ses plus récentes éditions) est le premier roman de Franz Kafka (1883-1924)."
|
||||||
|
}`
|
||||||
|
id := testPostBookHandler(t, bookJson, 200)
|
||||||
|
createdBook := testGetBook(t, strconv.FormatUint(uint64(id), 10), http.StatusOK)
|
||||||
|
|
||||||
|
assert.Equal(t,
|
||||||
|
dto.FullBookGet{
|
||||||
|
Title: "Amerika",
|
||||||
|
Author: "Kafka",
|
||||||
|
AuthorID: 27,
|
||||||
|
ISBN: "978-2-07-036803-7",
|
||||||
|
InventaireId: "isbn:9782070368037",
|
||||||
|
OpenLibraryId: "OL8838048M",
|
||||||
|
Summary: "L'Amérique (Amerika en version originale allemande) ou Le Disparu (Der Verschollene, titre voulu par l'auteur et rendu au livre dans ses plus récentes éditions) est le premier roman de Franz Kafka (1883-1924).",
|
||||||
|
Rating: 0,
|
||||||
|
Read: false,
|
||||||
|
CoverPath: "",
|
||||||
|
Review: "",
|
||||||
|
}, createdBook)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPostBookHandler_TitleTooLong(t *testing.T) {
|
func TestPostBookHandler_TitleTooLong(t *testing.T) {
|
||||||
bookJson :=
|
bookJson :=
|
||||||
`{
|
`{
|
||||||
@@ -63,7 +96,7 @@ func TestPostBookHandler_AuthorTooLong(t *testing.T) {
|
|||||||
testPostBookHandler(t, bookJson, 400)
|
testPostBookHandler(t, bookJson, 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPostBookHandler(t *testing.T, bookJson string, expectedCode int) {
|
func testPostBookHandler(t *testing.T, bookJson string, expectedCode int) uint {
|
||||||
router := testutils.TestSetup()
|
router := testutils.TestSetup()
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
@@ -75,4 +108,16 @@ func testPostBookHandler(t *testing.T, bookJson string, expectedCode int) {
|
|||||||
|
|
||||||
assert.Equal(t, expectedCode, w.Code)
|
assert.Equal(t, expectedCode, w.Code)
|
||||||
|
|
||||||
|
if w.Code != http.StatusOK {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var parsed struct {
|
||||||
|
ID uint
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.Unmarshal(w.Body.Bytes(), &parsed)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
return parsed.ID
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,11 @@ type BookSearchGetParam struct {
|
|||||||
type BookPostCreate struct {
|
type BookPostCreate 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"`
|
||||||
|
ISBN string `json:"isbn" binding:"max=18"`
|
||||||
|
InventaireID string `json:"inventaireid" binding:"max=50"`
|
||||||
|
OpenLibraryId string `json:"openlibraryid" binding:"max=50"`
|
||||||
|
ShortDescription string `json:"shortdescription" binding:"max=300"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
CoverID uint `json:"coverId"`
|
CoverID uint `json:"coverId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ package routes
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"git.artlef.fr/bibliomane/internal/appcontext"
|
"git.artlef.fr/bibliomane/internal/appcontext"
|
||||||
"git.artlef.fr/bibliomane/internal/dto"
|
"git.artlef.fr/bibliomane/internal/dto"
|
||||||
"git.artlef.fr/bibliomane/internal/model"
|
"git.artlef.fr/bibliomane/internal/model"
|
||||||
"git.artlef.fr/bibliomane/internal/myvalidator"
|
"git.artlef.fr/bibliomane/internal/myvalidator"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -28,28 +30,34 @@ func PostBookHandler(ac appcontext.AppContext) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = saveBookToDb(ac, book, &user)
|
id, err := saveBookToDb(ac, book, &user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ac.C.String(200, "Success")
|
ac.C.JSON(http.StatusOK, gin.H{"id": id})
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveBookToDb(ac appcontext.AppContext, b dto.BookPostCreate, user *model.User) error {
|
func saveBookToDb(ac appcontext.AppContext, b dto.BookPostCreate, user *model.User) (uint, error) {
|
||||||
author, err := fetchOrCreateAuthor(ac, b.Author)
|
author, err := fetchOrCreateAuthor(ac, b.Author)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return 0, err
|
||||||
}
|
}
|
||||||
book := model.Book{
|
book := model.Book{
|
||||||
Title: b.Title,
|
Title: b.Title,
|
||||||
AuthorID: author.ID,
|
AuthorID: author.ID,
|
||||||
|
ISBN: b.ISBN,
|
||||||
|
InventaireID: b.InventaireID,
|
||||||
|
OpenLibraryId: b.OpenLibraryId,
|
||||||
|
SmallDescription: b.ShortDescription,
|
||||||
|
Summary: b.Summary,
|
||||||
AddedBy: *user,
|
AddedBy: *user,
|
||||||
}
|
}
|
||||||
if b.CoverID > 0 {
|
if b.CoverID > 0 {
|
||||||
book.CoverID = b.CoverID
|
book.CoverID = b.CoverID
|
||||||
}
|
}
|
||||||
return ac.Db.Save(&book).Error
|
err = ac.Db.Save(&book).Error
|
||||||
|
return book.ID, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchOrCreateAuthor(ac appcontext.AppContext, name string) (*model.Author, error) {
|
func fetchOrCreateAuthor(ac appcontext.AppContext, name string) (*model.Author, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user