Collections: allow to drag and drop to change book position
This commit is contained in:
12
demodata.sql
12
demodata.sql
@@ -131,6 +131,7 @@ INSERT INTO collections(name, user_id) VALUES ('Littérature française',(SELECT
|
||||
INSERT INTO collections(name, user_id) VALUES ('Nouvelles',(SELECT id FROM users WHERE name = 'demo'));
|
||||
INSERT INTO collections(name, user_id) VALUES ('Non fiction',(SELECT id FROM users WHERE name = 'demo2'));
|
||||
INSERT INTO collections(name, user_id) VALUES ('Empty',(SELECT id FROM users WHERE name = 'demo'));
|
||||
INSERT INTO collections(name, user_id) VALUES ('Lu récemment',(SELECT id FROM users WHERE name = 'demo'));
|
||||
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Nord'), 1);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Littérature française'), (SELECT id FROM books WHERE title = 'Gargantua'), 2);
|
||||
@@ -142,8 +143,17 @@ INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT i
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Dojoji et autres nouvelles'), 1);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le meurtre d''O-tsuya'), 2);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Le coup de pistolet'), 3);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Duo'), 3);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Nouvelles'), (SELECT id FROM books WHERE title = 'Duo'), 4);
|
||||
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'Recherches philosophiques'), 1);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'De sang-froid'), 2);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Non fiction'), (SELECT id FROM books WHERE title = 'The Life of Jesus'), 3);
|
||||
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'L''Homme sans qualités, tome 1'), 1);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'Iliade'), 2);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'Duo'), 3);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'De sang-froid'), 4);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'Le Pavillon d''or'), 5);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'Recherches philosophiques'), 6);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'Dojoji et autres nouvelles'), 7);
|
||||
INSERT INTO collection_items(collection_id, book_id, position) VALUES ((SELECT id FROM collections WHERE name = 'Lu récemment'), (SELECT id FROM books WHERE title = 'Le château'), 8);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { getCollection } from './api.js'
|
||||
import { getCollection, postCollectionChangePosition } from './api.js'
|
||||
import CollectionFormElement from './CollectionFormElement.vue'
|
||||
import AddBookToCollection from './AddBookToCollection.vue'
|
||||
import Pagination from './Pagination.vue'
|
||||
@@ -17,6 +17,10 @@ const offset = computed(() => (pageNumber.value - 1) * limit)
|
||||
const data = ref(null)
|
||||
const error = ref(null)
|
||||
|
||||
const itemIdBeingGrabbed = ref(null)
|
||||
|
||||
const itemIdBeingOvered = ref(null)
|
||||
|
||||
let totalElementsNumber = computed(() =>
|
||||
typeof data != 'undefined' && data.value != null ? data.value['count'] : 0,
|
||||
)
|
||||
@@ -35,15 +39,59 @@ function pageChange(newPageNumber) {
|
||||
function fetchCollection() {
|
||||
pageChange(1)
|
||||
}
|
||||
|
||||
function onDragStart(event, id) {
|
||||
event.dataTransfer.effectAllowed = 'move'
|
||||
// Custom type to identify a collectionitem drag
|
||||
event.dataTransfer.setData('collectionitem', '')
|
||||
itemIdBeingGrabbed.value = id
|
||||
}
|
||||
|
||||
function onDragover(event) {
|
||||
if (event.dataTransfer.types.includes('collectionitem')) {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
function onDrop(id, position) {
|
||||
if (id == itemIdBeingGrabbed.value) {
|
||||
//nothing to do
|
||||
return
|
||||
}
|
||||
postCollectionChangePosition(props.id, itemIdBeingGrabbed.value, position).then((res) => {
|
||||
if (res.ok) {
|
||||
fetchCollection()
|
||||
} else {
|
||||
res.json().then((json) => {
|
||||
error.value = json
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function onDragend() {
|
||||
itemIdBeingGrabbed.value = null
|
||||
itemIdBeingOvered.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="error">{{ $t('bookform.error', { error: error.message }) }}</div>
|
||||
<div v-if="error">{{ $t('collection.error', { error: error }) }}</div>
|
||||
<div v-if="data">
|
||||
<h2 class="title">{{ data.name }}</h2>
|
||||
<AddBookToCollection :collection-id="props.id" @created="fetchCollection" />
|
||||
<div>
|
||||
<CollectionFormElement v-for="item in data.items" :key="item.id" v-bind="item" />
|
||||
<CollectionFormElement
|
||||
@drop="onDrop(item.id, item.position)"
|
||||
@dragstart="(e) => onDragStart(e, item.id)"
|
||||
@dragend="onDragend"
|
||||
@dragover="onDragover"
|
||||
@dragenter="itemIdBeingOvered = item.id"
|
||||
v-for="item in data.items"
|
||||
:key="item.id"
|
||||
:is-dragover="itemIdBeingOvered === item.id"
|
||||
v-bind="item"
|
||||
/>
|
||||
</div>
|
||||
<Pagination
|
||||
class="mt-5"
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
import BookListElement from './BookListElement.vue'
|
||||
|
||||
const props = defineProps({
|
||||
isDragover: Boolean,
|
||||
id: Number,
|
||||
position: Number,
|
||||
book: Array,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="collectionitembox">
|
||||
<div :class="isDragover ? 'dragover' : ''" draggable="true" class="collectionitembox">
|
||||
<BookListElement v-bind="props.book">
|
||||
<div class="separator" />
|
||||
<div class="positionindicator centered is-narrow">
|
||||
@@ -50,9 +52,15 @@ const props = defineProps({
|
||||
margin-right: 30px;
|
||||
border-top-right-radius: var(--bulma-box-radius);
|
||||
border-bottom-right-radius: var(--bulma-box-radius);
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.positionwidget:hover {
|
||||
cursor: move;
|
||||
.positionwidget:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.dragover {
|
||||
border: 3px solid var(--bulma-primary);
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -130,7 +130,7 @@ export function postCollection(collection) {
|
||||
return genericPayloadCall('/ws/collection', collection, 'POST')
|
||||
}
|
||||
|
||||
export function postCollectionAddBook(collectionId, bookId) {
|
||||
export function postCollectionAddBook(collectionId, position) {
|
||||
return genericPayloadCall(
|
||||
'/ws/collection/' + collectionId + '/addbook',
|
||||
{ bookId: bookId },
|
||||
@@ -138,6 +138,14 @@ export function postCollectionAddBook(collectionId, bookId) {
|
||||
)
|
||||
}
|
||||
|
||||
export function postCollectionChangePosition(collectionId, itemId, position) {
|
||||
return genericPayloadCall(
|
||||
'/ws/collection/' + collectionId + '/changeposition',
|
||||
{ itemId: itemId, position: position },
|
||||
'POST',
|
||||
)
|
||||
}
|
||||
|
||||
export function putBook(id, book) {
|
||||
return genericPayloadCall('/ws/book/edit/' + id, book.value, 'PUT')
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ func CollectionItemsQueryToDto(itemsQueryResult []query.CollectionItemQueryResul
|
||||
}
|
||||
dtoItems = append(dtoItems,
|
||||
dto.CollectionItemGet{
|
||||
ID: queryResult.ItemID,
|
||||
Position: queryResult.Position,
|
||||
Book: bookItem,
|
||||
})
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
func TestFetchAllCollections_OK(t *testing.T) {
|
||||
status, res := testFetchCollections(t, "10", "0")
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
assert.Equal(t, int64(3), res.Count)
|
||||
assert.Equal(t, 3, len(res.Collections))
|
||||
assert.Equal(t, int64(4), res.Count)
|
||||
assert.Equal(t, 4, len(res.Collections))
|
||||
}
|
||||
|
||||
func testFetchCollections(t *testing.T, limit string, offset string) (int, dto.CollectionItemsGet) {
|
||||
|
||||
113
internal/apitest/post_collection_changeposition_test.go
Normal file
113
internal/apitest/post_collection_changeposition_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package apitest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.artlef.fr/bibliomane/internal/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPostCollectionChangePositionHandler_PositionOk(t *testing.T) {
|
||||
payload :=
|
||||
`{
|
||||
"itemId": 14,
|
||||
"position": 2
|
||||
}`
|
||||
collectionId := "5"
|
||||
status := testPostCollectionChangePositionHandler(collectionId, payload)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
_, collection := testGetCollection(t, collectionId, "10", "0")
|
||||
assert.Equal(t, uint(1), collection.Items[0].Position)
|
||||
assert.Equal(t, uint(2), collection.Items[1].Position)
|
||||
assert.Equal(t, uint(3), collection.Items[2].Position)
|
||||
assert.Equal(t, uint(4), collection.Items[3].Position)
|
||||
}
|
||||
|
||||
func TestPostCollectionChangePositionHandler_ChangeOtherElement(t *testing.T) {
|
||||
payload :=
|
||||
`{
|
||||
"itemId": 17,
|
||||
"position": 3
|
||||
}`
|
||||
collectionId := "5"
|
||||
status := testPostCollectionChangePositionHandler(collectionId, payload)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
_, collection := testGetCollection(t, collectionId, "10", "0")
|
||||
assert.Equal(t, "Duo", collection.Items[3].Book.Title)
|
||||
}
|
||||
|
||||
func TestPostCollectionChangePositionHandler_LastPosition(t *testing.T) {
|
||||
payload :=
|
||||
`{
|
||||
"itemId": 19,
|
||||
"position": 546
|
||||
}`
|
||||
collectionId := "5"
|
||||
status := testPostCollectionChangePositionHandler(collectionId, payload)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
_, collection := testGetCollection(t, collectionId, "10", "0")
|
||||
assert.Equal(t, "Recherches philosophiques", collection.Items[7].Book.Title)
|
||||
assert.Equal(t, "Le château", collection.Items[6].Book.Title)
|
||||
}
|
||||
|
||||
func TestPostCollectionChangePositionHandler_FirstPosition(t *testing.T) {
|
||||
payload :=
|
||||
`{
|
||||
"itemId": 16,
|
||||
"position": 1
|
||||
}`
|
||||
collectionId := "5"
|
||||
status := testPostCollectionChangePositionHandler(collectionId, payload)
|
||||
assert.Equal(t, http.StatusOK, status)
|
||||
_, collection := testGetCollection(t, collectionId, "10", "0")
|
||||
assert.Equal(t, "Duo", collection.Items[0].Book.Title)
|
||||
}
|
||||
|
||||
func TestPostCollectionChangePositionHandler_WrongPosition(t *testing.T) {
|
||||
payload :=
|
||||
`{
|
||||
"itemId": 9,
|
||||
"position": 0
|
||||
}`
|
||||
collectionId := "5"
|
||||
status := testPostCollectionChangePositionHandler(collectionId, payload)
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
}
|
||||
|
||||
func TestPostCollectionChangePositionHandler_MissingPosition(t *testing.T) {
|
||||
payload :=
|
||||
`{
|
||||
"itemId": 9
|
||||
}`
|
||||
collectionId := "5"
|
||||
status := testPostCollectionChangePositionHandler(collectionId, payload)
|
||||
assert.Equal(t, http.StatusBadRequest, status)
|
||||
}
|
||||
|
||||
func TestPostCollectionChangePositionWrongCollection(t *testing.T) {
|
||||
payload :=
|
||||
`{
|
||||
"itemId": 1,
|
||||
"position": 9
|
||||
}`
|
||||
collectionId := "5"
|
||||
status := testPostCollectionChangePositionHandler(collectionId, payload)
|
||||
assert.Equal(t, http.StatusInternalServerError, status)
|
||||
}
|
||||
|
||||
func testPostCollectionChangePositionHandler(collectionId string, payload string) int {
|
||||
router := testutils.TestSetup()
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
token := testutils.ConnectDemoUser(router)
|
||||
req, _ := http.NewRequest("POST", "/ws/collection/"+collectionId+"/changeposition",
|
||||
strings.NewReader(payload))
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
router.ServeHTTP(w, req)
|
||||
|
||||
return w.Code
|
||||
}
|
||||
@@ -51,6 +51,11 @@ type CollectionBook struct {
|
||||
BookID uint `json:"bookId" binding:"required"`
|
||||
}
|
||||
|
||||
type CollectionItemPosition struct {
|
||||
Position uint `json:"position" binding:"required,gte=1"`
|
||||
ItemID uint `json:"itemId" binding:"required"`
|
||||
}
|
||||
|
||||
type FileInfoPost struct {
|
||||
FileID uint `json:"fileId"`
|
||||
FilePath string `json:"filepath"`
|
||||
|
||||
@@ -51,6 +51,7 @@ type CollectionGet struct {
|
||||
}
|
||||
|
||||
type CollectionItemGet struct {
|
||||
ID uint `json:"id"`
|
||||
Position uint `json:"position"`
|
||||
Book BookItemGet `json:"book"`
|
||||
}
|
||||
|
||||
@@ -10,3 +10,4 @@ RegistrationDisabled = "Registration has been disabled on this instance."
|
||||
UserAlreadyExists = "An user with this name already exists."
|
||||
ErrorWhenCreatingUserFromStr = "Error when creating user from string %s"
|
||||
Unauthorized = "You are not allowed to access this document."
|
||||
ItemDoesNotBelongToCollection = "Item does not belong to the collection."
|
||||
|
||||
@@ -10,3 +10,4 @@ RegistrationDisabled = "La création de nouveaux comptes a été désactivée su
|
||||
UserAlreadyExists = "Un utilisateur avec le même nom existe déjà."
|
||||
ErrorWhenCreatingUserFromStr = "Erreur lors de la création de l'utilisateur %s"
|
||||
Unauthorized = "Vous n'êtes pas autorisé à accéder à cette page."
|
||||
ItemDoesNotBelongToCollection = "Cet élément n'appartient pas à la liste."
|
||||
|
||||
@@ -81,6 +81,7 @@ func fetchCollections(db *gorm.DB, userId uint) *gorm.DB {
|
||||
}
|
||||
|
||||
type CollectionItemQueryResult struct {
|
||||
ItemID uint
|
||||
Position uint
|
||||
ID uint
|
||||
Title string
|
||||
@@ -113,7 +114,7 @@ func FetchCollectionBooksCount(db *gorm.DB, userId uint, collectionId uint) (int
|
||||
|
||||
func fetchCollectionBooksQuery(db *gorm.DB, userId uint, collectionId uint) *gorm.DB {
|
||||
query := db.Model(&model.CollectionItem{})
|
||||
query = query.Select("collection_items.position, " + selectBookItem())
|
||||
query = query.Select("collection_items.position, collection_items.ID as item_id, " + selectBookItem())
|
||||
query = query.Joins("left join collections on (collection_items.collection_id = collections.id)")
|
||||
query = query.Joins("left join books on (books.id = collection_items.book_id)")
|
||||
query = joinAuthors(query)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"git.artlef.fr/bibliomane/internal/model"
|
||||
"git.artlef.fr/bibliomane/internal/myvalidator"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func PostCollectionBookHandler(ac appcontext.AppContext) {
|
||||
@@ -56,7 +57,16 @@ func PostCollectionBookHandler(ac appcontext.AppContext) {
|
||||
return
|
||||
}
|
||||
|
||||
item := model.CollectionItem{Position: 0, BookID: book.ID, CollectionID: collection.ID}
|
||||
//reorder other items
|
||||
q := ac.Db.Model(&model.CollectionItem{})
|
||||
q = q.Where("collection_id = ?", collection.ID)
|
||||
err = q.UpdateColumn("position", gorm.Expr("position + 1")).Error
|
||||
if err != nil {
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
|
||||
item := model.CollectionItem{Position: 1, BookID: book.ID, CollectionID: collection.ID}
|
||||
ac.Db.Save(&item)
|
||||
ac.C.String(http.StatusOK, "Success")
|
||||
}
|
||||
|
||||
106
internal/routes/collectionchangepositionpost.go
Normal file
106
internal/routes/collectionchangepositionpost.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.artlef.fr/bibliomane/internal/appcontext"
|
||||
"git.artlef.fr/bibliomane/internal/dto"
|
||||
"git.artlef.fr/bibliomane/internal/i18nresource"
|
||||
"git.artlef.fr/bibliomane/internal/model"
|
||||
"git.artlef.fr/bibliomane/internal/myvalidator"
|
||||
"git.artlef.fr/bibliomane/internal/query"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func PostCollectionChangePositionHandler(ac appcontext.AppContext) {
|
||||
collectionId, err := strconv.ParseUint(ac.C.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
ac.C.JSON(http.StatusBadRequest, gin.H{"error": err})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := ac.GetAuthenticatedUser()
|
||||
if err != nil {
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
|
||||
var collection model.Collection
|
||||
err = ac.Db.First(&collection, collectionId).Error
|
||||
if err != nil {
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
|
||||
if collection.UserID != user.ID {
|
||||
err := myvalidator.HttpError{
|
||||
StatusCode: http.StatusUnauthorized,
|
||||
Err: errors.New(i18nresource.GetTranslatedMessage(&ac, "Unauthorized")),
|
||||
}
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
|
||||
var collectionBookPosition dto.CollectionItemPosition
|
||||
err = ac.C.ShouldBindJSON(&collectionBookPosition)
|
||||
if err != nil {
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
|
||||
var item model.CollectionItem
|
||||
err = ac.Db.First(&item, collectionBookPosition.ItemID).Error
|
||||
if err != nil {
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
|
||||
if collection.ID != item.CollectionID {
|
||||
err := myvalidator.HttpError{
|
||||
StatusCode: http.StatusInternalServerError,
|
||||
Err: errors.New(i18nresource.GetTranslatedMessage(&ac, "ItemDoesNotBelongToCollection")),
|
||||
}
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
count, err := query.FetchCollectionBooksCount(ac.Db, user.ID, item.CollectionID)
|
||||
if err != nil {
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
newPosition := collectionBookPosition.Position
|
||||
if int64(newPosition) > count {
|
||||
newPosition = uint(count)
|
||||
}
|
||||
|
||||
if item.Position == collectionBookPosition.Position {
|
||||
//nothing to do
|
||||
ac.C.String(http.StatusOK, "Success")
|
||||
return
|
||||
}
|
||||
lowerPosition := item.Position + 1
|
||||
higherPosition := item.Position - 1
|
||||
operationToDo := ""
|
||||
if item.Position < collectionBookPosition.Position {
|
||||
higherPosition = collectionBookPosition.Position
|
||||
operationToDo = "position - 1"
|
||||
} else {
|
||||
lowerPosition = collectionBookPosition.Position
|
||||
operationToDo = "position + 1"
|
||||
}
|
||||
|
||||
q := ac.Db.Model(&model.CollectionItem{})
|
||||
q = q.Where("collection_id = ? AND position BETWEEN ? AND ?", collection.ID, lowerPosition, higherPosition)
|
||||
err = q.UpdateColumn("position", gorm.Expr(operationToDo)).Error
|
||||
if err != nil {
|
||||
myvalidator.ReturnErrorsAsJsonResponse(&ac, err)
|
||||
return
|
||||
}
|
||||
|
||||
item.Position = collectionBookPosition.Position
|
||||
ac.Db.Save(&item)
|
||||
ac.C.String(http.StatusOK, "Success")
|
||||
}
|
||||
@@ -87,6 +87,9 @@ func Setup(config *config.Config) *gin.Engine {
|
||||
ws.POST("/collection/:id/addbook", func(c *gin.Context) {
|
||||
routes.PostCollectionBookHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
|
||||
})
|
||||
ws.POST("/collection/:id/changeposition", func(c *gin.Context) {
|
||||
routes.PostCollectionChangePositionHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
|
||||
})
|
||||
ws.POST("/collection", func(c *gin.Context) {
|
||||
routes.PostCollectionHandler(appcontext.AppContext{C: c, Db: db, I18n: bundle, Config: config})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user