]> source.dussan.org Git - gitea.git/commitdiff
Sort / Move project boards (#14634)
authorRomain <romdum@users.noreply.github.com>
Thu, 11 Feb 2021 16:32:27 +0000 (17:32 +0100)
committerGitHub <noreply@github.com>
Thu, 11 Feb 2021 16:32:27 +0000 (17:32 +0100)
Sort Project board (#14533)

models/project_board.go
modules/forms/repo_form.go
routers/repo/projects.go
routers/routes/web.go
templates/repo/projects/view.tmpl
web_src/js/features/projects.js

index a9c0b3ed8bb04992e22048eb3c6dc55f1081aade..e56bf8f819919d838aa8ca8a8b7c913f80937832 100644 (file)
@@ -36,6 +36,7 @@ type ProjectBoard struct {
        ID      int64 `xorm:"pk autoincr"`
        Title   string
        Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
+       Sorting int8 `xorm:"DEFAULT 0"`
 
        ProjectID int64 `xorm:"INDEX NOT NULL"`
        CreatorID int64 `xorm:"NOT NULL"`
@@ -157,15 +158,24 @@ func getProjectBoard(e Engine, boardID int64) (*ProjectBoard, error) {
        return board, nil
 }
 
-// UpdateProjectBoard updates the title of a project board
+// UpdateProjectBoard updates a project board
 func UpdateProjectBoard(board *ProjectBoard) error {
        return updateProjectBoard(x, board)
 }
 
 func updateProjectBoard(e Engine, board *ProjectBoard) error {
-       _, err := e.ID(board.ID).Cols(
-               "title",
-       ).Update(board)
+       var fieldToUpdate []string
+
+       if board.Sorting != 0 {
+               fieldToUpdate = append(fieldToUpdate, "sorting")
+       }
+
+       if board.Title != "" {
+               fieldToUpdate = append(fieldToUpdate, "title")
+       }
+
+       _, err := e.ID(board.ID).Cols(fieldToUpdate...).Update(board)
+
        return err
 }
 
@@ -178,7 +188,7 @@ func GetProjectBoards(projectID int64) (ProjectBoardList, error) {
 func getProjectBoards(e Engine, projectID int64) ([]*ProjectBoard, error) {
        var boards = make([]*ProjectBoard, 0, 5)
 
-       if err := e.Where("project_id=? AND `default`=?", projectID, false).Find(&boards); err != nil {
+       if err := e.Where("project_id=? AND `default`=?", projectID, false).OrderBy("Sorting").Find(&boards); err != nil {
                return nil, err
        }
 
@@ -277,3 +287,17 @@ func (bs ProjectBoardList) LoadIssues() (IssueList, error) {
        }
        return issues, nil
 }
+
+// UpdateProjectBoardSorting update project board sorting
+func UpdateProjectBoardSorting(bs ProjectBoardList) error {
+       for i := range bs {
+               _, err := x.ID(bs[i].ID).Cols(
+                       "sorting",
+               ).Update(bs[i])
+
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}
index ac968a1dd5b67ba2965980c5f907df95ab48e422..f177b21f05ac6cc0e8dda65b2585261c4eeaab27 100644 (file)
@@ -487,10 +487,10 @@ type UserCreateProjectForm struct {
        UID       int64 `binding:"Required"`
 }
 
-// EditProjectBoardTitleForm is a form for editing the title of a project's
-// board
-type EditProjectBoardTitleForm struct {
-       Title string `binding:"Required;MaxSize(100)"`
+// EditProjectBoardForm is a form for editing a project board
+type EditProjectBoardForm struct {
+       Title   string `binding:"Required;MaxSize(100)"`
+       Sorting int8
 }
 
 //    _____  .__.__                   __
index 49bcfef0ce5b103d06d77a81a4e67408b23b196e..4aa03e9efcb2613a9b1c37a986cb41c427adbaf8 100644 (file)
@@ -403,7 +403,7 @@ func DeleteProjectBoard(ctx *context.Context) {
 
 // AddBoardToProjectPost allows a new board to be added to a project.
 func AddBoardToProjectPost(ctx *context.Context) {
-       form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm)
+       form := web.GetForm(ctx).(*auth.EditProjectBoardForm)
        if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(models.AccessModeWrite, models.UnitTypeProjects) {
                ctx.JSON(403, map[string]string{
                        "message": "Only authorized users are allowed to perform this action.",
@@ -481,9 +481,9 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*models.Project,
        return project, board
 }
 
-// EditProjectBoardTitle allows a project board's title to be updated
-func EditProjectBoardTitle(ctx *context.Context) {
-       form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm)
+// EditProjectBoard allows a project board's to be updated
+func EditProjectBoard(ctx *context.Context) {
+       form := web.GetForm(ctx).(*auth.EditProjectBoardForm)
        _, board := checkProjectBoardChangePermissions(ctx)
        if ctx.Written() {
                return
@@ -493,6 +493,10 @@ func EditProjectBoardTitle(ctx *context.Context) {
                board.Title = form.Title
        }
 
+       if form.Sorting != 0 {
+               board.Sorting = form.Sorting
+       }
+
        if err := models.UpdateProjectBoard(board); err != nil {
                ctx.ServerError("UpdateProjectBoard", err)
                return
index 1f860a6239497098476f17b998ce9e85b9a22311..9e3e690fb965ff80349ffcb390a6017142337350 100644 (file)
@@ -853,7 +853,7 @@ func RegisterRoutes(m *web.Route) {
                                m.Get("/new", repo.NewProject)
                                m.Post("/new", bindIgnErr(auth.CreateProjectForm{}), repo.NewProjectPost)
                                m.Group("/{id}", func() {
-                                       m.Post("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.AddBoardToProjectPost)
+                                       m.Post("", bindIgnErr(auth.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
                                        m.Post("/delete", repo.DeleteProject)
 
                                        m.Get("/edit", repo.EditProject)
@@ -861,7 +861,7 @@ func RegisterRoutes(m *web.Route) {
                                        m.Post("/{action:open|close}", repo.ChangeProjectStatus)
 
                                        m.Group("/{boardID}", func() {
-                                               m.Put("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.EditProjectBoardTitle)
+                                               m.Put("", bindIgnErr(auth.EditProjectBoardForm{}), repo.EditProjectBoard)
                                                m.Delete("", repo.DeleteProjectBoard)
                                                m.Post("/default", repo.SetDefaultProjectBoard)
 
index 9b2aa4bc7d04df00d5fb0c0fe6eaa2af55c2eb6d..de1fc37b03fa49e0a5b82d9e748de9319a7b8cfa 100644 (file)
@@ -72,7 +72,7 @@
                <div class="board">
                        {{ range $board := .Boards }}
 
-                       <div class="ui segment board-column">
+                       <div class="ui segment board-column" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">
                                <div class="board-column-header">
                                        <div class="ui large label board-label">{{.Title}}</div>
                                        {{if and $.CanWriteProjects (not $.Repository.IsArchived) $.PageIsProjects (ne .ID 0)}}
index b5f52f7443891694aa143ab0862d1e28910ef223..254079b7693b3e6ecb3132170ef0aa2d00dd4c7d 100644 (file)
@@ -8,6 +8,34 @@ export default async function initProject() {
   const {Sortable} = await import(/* webpackChunkName: "sortable" */'sortablejs');
   const boardColumns = document.getElementsByClassName('board-column');
 
+  new Sortable(
+    document.getElementsByClassName('board')[0],
+    {
+      group: 'board-column',
+      draggable: '.board-column',
+      animation: 150,
+      onSort: () => {
+        const board = document.getElementsByClassName('board')[0];
+        const boardColumns = board.getElementsByClassName('board-column');
+
+        boardColumns.forEach((column, i) => {
+          if (parseInt($(column).data('sorting')) !== i) {
+            $.ajax({
+              url: $(column).data('url'),
+              data: JSON.stringify({sorting: i}),
+              headers: {
+                'X-Csrf-Token': csrf,
+                'X-Remote': true,
+              },
+              contentType: 'application/json',
+              method: 'PUT',
+            });
+          }
+        });
+      },
+    },
+  );
+
   for (const column of boardColumns) {
     new Sortable(
       column.getElementsByClassName('board')[0],
@@ -74,6 +102,7 @@ export default async function initProject() {
 
     window.location.reload();
   });
+
   $('.delete-project-board').each(function () {
     $(this).click(function (e) {
       e.preventDefault();