From d7ba3cf12e62390e00db5a51c8e13b5ff41e5178 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Fri, 6 Aug 2021 17:47:38 +0200
Subject: [PATCH 01/25] #11160 Add colors column for ProjectBoard

---
 models/project_board.go      | 5 +++++
 routers/web/repo/projects.go | 5 +++++
 services/forms/repo_form.go  | 1 +
 3 files changed, 11 insertions(+)

diff --git a/models/project_board.go b/models/project_board.go
index 4b313ed8f0cb8..54f596d5eb142 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -37,6 +37,7 @@ type ProjectBoard struct {
 	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:"NOT NULL DEFAULT 0"`
+	Color   string
 
 	ProjectID int64 `xorm:"INDEX NOT NULL"`
 	CreatorID int64 `xorm:"NOT NULL"`
@@ -173,6 +174,10 @@ func updateProjectBoard(e Engine, board *ProjectBoard) error {
 		fieldToUpdate = append(fieldToUpdate, "title")
 	}
 
+	if board.Color != "" {
+		fieldToUpdate = append(fieldToUpdate, "color")
+	}
+
 	_, err := e.ID(board.ID).Cols(fieldToUpdate...).Update(board)
 
 	return err
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 9afbf0165d5d9..9951cb48f08bc 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -442,6 +442,7 @@ func AddBoardToProjectPost(ctx *context.Context) {
 	if err := models.NewProjectBoard(&models.ProjectBoard{
 		ProjectID: project.ID,
 		Title:     form.Title,
+		Color:     form.Color,
 		CreatorID: ctx.User.ID,
 	}); err != nil {
 		ctx.ServerError("NewProjectBoard", err)
@@ -511,6 +512,10 @@ func EditProjectBoard(ctx *context.Context) {
 		board.Title = form.Title
 	}
 
+	if form.Color != "" {
+		board.Color = form.Color
+	}
+
 	if form.Sorting != 0 {
 		board.Sorting = form.Sorting
 	}
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index c1c146f234668..59cc0ac268d3c 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -498,6 +498,7 @@ type UserCreateProjectForm struct {
 type EditProjectBoardForm struct {
 	Title   string `binding:"Required;MaxSize(100)"`
 	Sorting int8
+	Color   string `binding:"MaxSize(7)"`
 }
 
 //    _____  .__.__                   __

From b2c13e07e8a4ef96b0f1d8eeaabf9db233c8519c Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Fri, 6 Aug 2021 17:48:06 +0200
Subject: [PATCH 02/25] #11160 Add color input in edit project board form

---
 templates/repo/projects/view.tmpl | 7 ++++++-
 web_src/js/features/projects.js   | 5 ++++-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index 7f0ac790decba..0db29e33d2b31 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -69,7 +69,7 @@
 		<div class="board">
 			{{ range $board := .Boards }}
 
-			<div class="ui segment board-column" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">
+			<div class="ui segment board-column" style="background: {{.Color}}!important;" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">
 				<div class="board-column-header df ac sb">
 					<div class="ui large label board-label py-2">{{.Title}}</div>
 					{{if and $.CanWriteProjects (not $.Repository.IsArchived) $.PageIsProjects (ne .ID 0)}}
@@ -104,6 +104,11 @@
 												<input class="project-board-title" id="new_board_title" name="title" value="{{.Title}}" required>
 											</div>
 
+											<div class="field">
+												<label for="new_board_color">{{$.i18n.Tr "repo.projects.board.edit_color"}}</label>
+												<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color" name="color" value="{{.Color}}">
+											</div>
+
 											<div class="text right actions">
 												<div class="ui cancel button">{{$.i18n.Tr "settings.cancel"}}</div>
 												<button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}" class="ui red button">{{$.i18n.Tr "repo.projects.board.edit"}}</button>
diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
index 0d619cab70ba5..424a545c1190e 100644
--- a/web_src/js/features/projects.js
+++ b/web_src/js/features/projects.js
@@ -66,6 +66,8 @@ export default async function initProject() {
     const projectTitleInput = $(this).find(
       '.content > .form > .field > .project-board-title',
     );
+    const projectColorInput = $(this).find('.content > .form > .field > #new_board_color');
+    const boardColumn = $(this).closest('.board-column');
 
     $(this)
       .find('.content > .form > .actions > .red')
@@ -74,7 +76,7 @@ export default async function initProject() {
 
         $.ajax({
           url: $(this).data('url'),
-          data: JSON.stringify({title: projectTitleInput.val()}),
+          data: JSON.stringify({title: projectTitleInput.val(), color: projectColorInput.val()}),
           headers: {
             'X-Csrf-Token': csrf,
             'X-Remote': true,
@@ -84,6 +86,7 @@ export default async function initProject() {
         }).done(() => {
           projectTitleLabel.text(projectTitleInput.val());
           projectTitleInput.closest('form').removeClass('dirty');
+          boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
           $('.ui.modal').modal('hide');
         });
       });

From 19bc8a73565324c981f707c3ae3540efa59b7c77 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Fri, 6 Aug 2021 19:01:11 +0200
Subject: [PATCH 03/25] Add label for color

---
 options/locale/locale_en-US.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index ccf19293ff8bf..77f81ddf32fdd 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1089,6 +1089,7 @@ projects.board.set_default = "Set Default"
 projects.board.set_default_desc = "Set this board as default for uncategorized issues and pulls"
 projects.board.delete = "Delete Board"
 projects.board.deletion_desc = "Deleting a project board moves all related issues to 'Uncategorized'. Continue?"
+projects.board.edit_color = "Color"
 projects.open = Open
 projects.close = Close
 

From bfbbbdaca01896c3c62a68e3667241af1cfe6782 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sat, 7 Aug 2021 12:10:16 +0200
Subject: [PATCH 04/25] Add color picker

---
 templates/repo/projects/view.tmpl   |  2 +-
 web_src/js/index.js                 | 22 ++++++++++++++++------
 web_src/less/features/projects.less | 13 +++++++++++++
 3 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index 0db29e33d2b31..caefe4b64307e 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -104,7 +104,7 @@
 												<input class="project-board-title" id="new_board_title" name="title" value="{{.Title}}" required>
 											</div>
 
-											<div class="field">
+											<div class="field color-field">
 												<label for="new_board_color">{{$.i18n.Tr "repo.projects.board.edit_color"}}</label>
 												<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color" name="color" value="{{.Color}}">
 											</div>
diff --git a/web_src/js/index.js b/web_src/js/index.js
index 17bf31d6a6673..9316cfb918925 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -159,13 +159,8 @@ function initLabelEdit() {
     $newLabelPanel.hide();
   });
 
-  createColorPicker($('.color-picker'));
+  initColorPicker();
 
-  $('.precolors .color').on('click', function () {
-    const color_hex = $(this).data('color-hex');
-    $('.color-picker').val(color_hex);
-    $('.minicolors-swatch-color').css('background-color', color_hex);
-  });
   $('.edit-label-button').on('click', function () {
     $('.edit-label .color-picker').minicolors('value', $(this).data('color'));
     $('#label-modal-id').val($(this).data('id'));
@@ -182,6 +177,16 @@ function initLabelEdit() {
   });
 }
 
+function initColorPicker() {
+  createColorPicker($('.color-picker'));
+
+  $('.precolors .color').on('click', function () {
+    const color_hex = $(this).data('color-hex');
+    $('.color-picker').val(color_hex);
+    $('.minicolors-swatch-color').css('background-color', color_hex);
+  });
+}
+
 function updateIssuesMeta(url, action, issueIds, elementId) {
   return new Promise(((resolve) => {
     $.ajax({
@@ -2729,6 +2734,11 @@ $(document).ready(async () => {
   });
   $('.show-modal.button').on('click', function () {
     $($(this).data('modal')).modal('show');
+    
+    let colorPickers = $($(this).data('modal')).find(".color-picker");
+    if (colorPickers.length > 0) {
+      initColorPicker();
+    }
   });
   $('.delete-post.button').on('click', function () {
     const $this = $(this);
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index f9d97a208696f..cd8e660c1d49c 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -81,3 +81,16 @@
 .card-ghost * {
   opacity: 0;
 }
+
+.color-field .minicolors.minicolors-theme-default {
+  display: block;
+
+  .minicolors-input {
+    height: 38px;
+    padding-left: 2rem;
+  }
+
+  .minicolors-swatch {
+    top: 10px;
+  }
+}
\ No newline at end of file

From 1d4d8e7c1a1e9aa237164fb075fe0f4840d5e706 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sat, 7 Aug 2021 12:19:16 +0200
Subject: [PATCH 05/25] Add color input on new board form

---
 templates/repo/projects/view.tmpl | 5 +++++
 web_src/js/features/projects.js   | 3 ++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index caefe4b64307e..e1132198dc6b5 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -21,6 +21,11 @@
 								<input class="new-board" id="new_board" name="title" required>
 							</div>
 
+							<div class="field color-field">
+								<label for="new_board_color">{{$.i18n.Tr "repo.projects.board.edit_color"}}</label>
+								<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color_picker" name="color">
+							</div>
+
 							<div class="text right actions">
 								<div class="ui cancel button">{{$.i18n.Tr "settings.cancel"}}</div>
 								<button data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}" class="ui green button" id="new_board_submit">{{$.i18n.Tr "repo.projects.board.new_submit"}}</button>
diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
index 424a545c1190e..f7518f215d1c9 100644
--- a/web_src/js/features/projects.js
+++ b/web_src/js/features/projects.js
@@ -130,10 +130,11 @@ export default async function initProject() {
     e.preventDefault();
 
     const boardTitle = $('#new_board');
+    const projectColorInput = $('#new_board_color_picker');
 
     $.ajax({
       url: $(this).data('url'),
-      data: JSON.stringify({title: boardTitle.val()}),
+      data: JSON.stringify({title: boardTitle.val(), color: projectColorInput.val()}),
       headers: {
         'X-Csrf-Token': csrf,
         'X-Remote': true,

From 1d0d1349ff34f1cc7179c531695b600851f0e9bc Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sat, 7 Aug 2021 12:27:01 +0200
Subject: [PATCH 06/25] Linter fix

---
 web_src/js/index.js                 | 3 +--
 web_src/less/features/projects.less | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/web_src/js/index.js b/web_src/js/index.js
index 9316cfb918925..146abd54cb3bc 100644
--- a/web_src/js/index.js
+++ b/web_src/js/index.js
@@ -2734,8 +2734,7 @@ $(document).ready(async () => {
   });
   $('.show-modal.button').on('click', function () {
     $($(this).data('modal')).modal('show');
-    
-    let colorPickers = $($(this).data('modal')).find(".color-picker");
+    const colorPickers = $($(this).data('modal')).find('.color-picker');
     if (colorPickers.length > 0) {
       initColorPicker();
     }
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index cd8e660c1d49c..a08207a694d07 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -93,4 +93,4 @@
   .minicolors-swatch {
     top: 10px;
   }
-}
\ No newline at end of file
+}

From ad2dc84c79b581f30f23e9bd8deff2e7c5b038ab Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sun, 8 Aug 2021 18:54:51 +0200
Subject: [PATCH 07/25] Add migration for color project board column

---
 models/migrations/migrations.go |  2 ++
 models/migrations/v191.go       | 22 ++++++++++++++++++++++
 2 files changed, 24 insertions(+)
 create mode 100644 models/migrations/v191.go

diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 9b04a364ca670..fcfcaab87881c 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -331,6 +331,8 @@ var migrations = []Migration{
 	NewMigration("Unwrap ldap.Sources", unwrapLDAPSourceCfg),
 	// v190 -> v191
 	NewMigration("Add agit flow pull request support", addAgitFlowPullRequest),
+	// v191 -> v192
+	NewMigration("Add Color to ProjectBoard table", addColorColToProjectBoard),
 }
 
 // GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/v191.go b/models/migrations/v191.go
new file mode 100644
index 0000000000000..c4ddfa8f73a56
--- /dev/null
+++ b/models/migrations/v191.go
@@ -0,0 +1,22 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"fmt"
+
+	"xorm.io/xorm"
+)
+
+func addColorColToProjectBoard(x *xorm.Engine) error {
+	type ProjectBoard struct {
+		Color string
+	}
+
+	if err := x.Sync2(new(ProjectBoard)); err != nil {
+		return fmt.Errorf("Sync2: %v", err)
+	}
+	return nil
+}

From 85b93462e049e245cb079b8f99fe97bcc3a649cf Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sun, 8 Aug 2021 19:17:24 +0200
Subject: [PATCH 08/25] validate project board column

---
 models/migrations/v191.go |  2 +-
 models/project_board.go   | 15 ++++++++++++---
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/models/migrations/v191.go b/models/migrations/v191.go
index c4ddfa8f73a56..c0332c7bb4ee5 100644
--- a/models/migrations/v191.go
+++ b/models/migrations/v191.go
@@ -12,7 +12,7 @@ import (
 
 func addColorColToProjectBoard(x *xorm.Engine) error {
 	type ProjectBoard struct {
-		Color string
+		Color string `xorm:"VARCHAR(7)"`
 	}
 
 	if err := x.Sync2(new(ProjectBoard)); err != nil {
diff --git a/models/project_board.go b/models/project_board.go
index 54f596d5eb142..1b7d33673feff 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -5,6 +5,9 @@
 package models
 
 import (
+	"fmt"
+	"regexp"
+
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 
@@ -31,13 +34,16 @@ const (
 	ProjectBoardTypeBugTriage
 )
 
+// LabelColorPattern is a regexp witch can validate LabelColor
+var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
+
 // ProjectBoard is used to represent boards on a project
 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:"NOT NULL DEFAULT 0"`
-	Color   string
+	Default bool   `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
+	Sorting int8   `xorm:"NOT NULL DEFAULT 0"`
+	Color   string `xorm:"VARCHAR(7)"`
 
 	ProjectID int64 `xorm:"INDEX NOT NULL"`
 	CreatorID int64 `xorm:"NOT NULL"`
@@ -175,6 +181,9 @@ func updateProjectBoard(e Engine, board *ProjectBoard) error {
 	}
 
 	if board.Color != "" {
+		if !BoardColorPattern.MatchString(board.Color) {
+			return fmt.Errorf("bad color code: %s", board.Color)
+		}
 		fieldToUpdate = append(fieldToUpdate, "color")
 	}
 

From ca4481a4e8022002d5133b482b8e1a8c8f52d331 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sun, 8 Aug 2021 19:26:24 +0200
Subject: [PATCH 09/25] Correct BoardColorPattern comment

---
 models/project_board.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/models/project_board.go b/models/project_board.go
index 1b7d33673feff..a5ae121dec80f 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -34,7 +34,7 @@ const (
 	ProjectBoardTypeBugTriage
 )
 
-// LabelColorPattern is a regexp witch can validate LabelColor
+// BoardColorPattern is a regexp witch can validate BoardColor
 var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
 
 // ProjectBoard is used to represent boards on a project

From b1822432f27b908a94dbab9d3d3ebe57645265f0 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sun, 8 Aug 2021 19:51:18 +0200
Subject: [PATCH 10/25] Create a generic "color" translation key

---
 options/locale/locale_en-US.ini            | 4 +---
 templates/repo/graph.tmpl                  | 2 +-
 templates/repo/projects/view.tmpl          | 4 ++--
 templates/repo/settings/webhook/slack.tmpl | 2 +-
 4 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 77f81ddf32fdd..7e1241ff91614 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -96,6 +96,7 @@ error = Error
 error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
 
 never = Never
+color = Color
 
 [error]
 occurred = An error has occurred
@@ -972,7 +973,6 @@ commit_graph = Commit Graph
 commit_graph.select = Select branches
 commit_graph.hide_pr_refs = Hide Pull Requests
 commit_graph.monochrome = Mono
-commit_graph.color = Color
 blame = Blame
 normal_view = Normal View
 line = line
@@ -1089,7 +1089,6 @@ projects.board.set_default = "Set Default"
 projects.board.set_default_desc = "Set this board as default for uncategorized issues and pulls"
 projects.board.delete = "Delete Board"
 projects.board.deletion_desc = "Deleting a project board moves all related issues to 'Uncategorized'. Continue?"
-projects.board.edit_color = "Color"
 projects.open = Open
 projects.close = Close
 
@@ -1786,7 +1785,6 @@ settings.slack_username = Username
 settings.slack_icon_url = Icon URL
 settings.discord_username = Username
 settings.discord_icon_url = Icon URL
-settings.slack_color = Color
 settings.event_desc = Trigger On:
 settings.event_push_only = Push Events
 settings.event_send_everything = All Events
diff --git a/templates/repo/graph.tmpl b/templates/repo/graph.tmpl
index e3b5cd38784e1..963bf0449ab44 100644
--- a/templates/repo/graph.tmpl
+++ b/templates/repo/graph.tmpl
@@ -47,7 +47,7 @@
 						</div>
 					</div>
 					<button id="flow-color-monochrome" class="ui labelled icon button{{if eq .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "repo.commit_graph.monochrome"}}">{{svg "material-invert-colors" 16 "mr-2"}}{{.i18n.Tr "repo.commit_graph.monochrome"}}</button>
-					<button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "repo.commit_graph.color"}}">{{svg "material-palette" 16 "mr-2"}}{{.i18n.Tr "repo.commit_graph.color"}}</button>
+					<button id="flow-color-colored" class="ui labelled icon button{{if ne .Mode "monochrome"}} active{{end}}" title="{{.i18n.Tr "color"}}">{{svg "material-palette" 16 "mr-2"}}{{.i18n.Tr "color"}}</button>
 				</div>
 			</h2>
 			<div class="ui dividing"></div>
diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index e1132198dc6b5..eb893bbcc2492 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -22,7 +22,7 @@
 							</div>
 
 							<div class="field color-field">
-								<label for="new_board_color">{{$.i18n.Tr "repo.projects.board.edit_color"}}</label>
+								<label for="new_board_color">{{$.i18n.Tr "color"}}</label>
 								<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color_picker" name="color">
 							</div>
 
@@ -110,7 +110,7 @@
 											</div>
 
 											<div class="field color-field">
-												<label for="new_board_color">{{$.i18n.Tr "repo.projects.board.edit_color"}}</label>
+												<label for="new_board_color">{{$.i18n.Tr "color"}}</label>
 												<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color" name="color" value="{{.Color}}">
 											</div>
 
diff --git a/templates/repo/settings/webhook/slack.tmpl b/templates/repo/settings/webhook/slack.tmpl
index d7b6eebf3ba6b..12e112acca448 100644
--- a/templates/repo/settings/webhook/slack.tmpl
+++ b/templates/repo/settings/webhook/slack.tmpl
@@ -20,7 +20,7 @@
 			<input id="icon_url" name="icon_url" value="{{.SlackHook.IconURL}}" placeholder="e.g. https://example.com/img/favicon.png">
 		</div>
 		<div class="field">
-			<label for="color">{{.i18n.Tr "repo.settings.slack_color"}}</label>
+			<label for="color">{{.i18n.Tr "color"}}</label>
 			<input id="color" name="color" value="{{.SlackHook.Color}}" placeholder="e.g. #dd4b39, good, warning, danger">
 		</div>
 		{{template "repo/settings/webhook/settings" .}}

From 77ed1e311d88d26bb20a228cfd101587f589762c Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Sun, 8 Aug 2021 20:19:23 +0200
Subject: [PATCH 11/25] Add color validation on new project board form

---
 models/project_board.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/models/project_board.go b/models/project_board.go
index a5ae121dec80f..969582597aad2 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -103,6 +103,11 @@ func createBoardsForProjectsType(sess *xorm.Session, project *Project) error {
 // NewProjectBoard adds a new project board to a given project
 func NewProjectBoard(board *ProjectBoard) error {
 	_, err := x.Insert(board)
+	if board.Color != "" {
+		if !BoardColorPattern.MatchString(board.Color) {
+			return fmt.Errorf("bad color code: %s", board.Color)
+		}
+	}
 	return err
 }
 

From e64812b8a92cae84791b08f13766c4ccbed2c673 Mon Sep 17 00:00:00 2001
From: 6543 <6543@obermui.de>
Date: Sun, 8 Aug 2021 22:51:40 +0200
Subject: [PATCH 12/25] Apply suggestions from code review

---
 models/project_board.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/models/project_board.go b/models/project_board.go
index 969582597aad2..ca6a3edcdedb4 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -102,12 +102,13 @@ func createBoardsForProjectsType(sess *xorm.Session, project *Project) error {
 
 // NewProjectBoard adds a new project board to a given project
 func NewProjectBoard(board *ProjectBoard) error {
-	_, err := x.Insert(board)
 	if board.Color != "" {
 		if !BoardColorPattern.MatchString(board.Color) {
 			return fmt.Errorf("bad color code: %s", board.Color)
 		}
 	}
+
+	_, err := x.Insert(board)
 	return err
 }
 

From ec952cdcba1b63cbcce395f8eb02ac6f94d433b0 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Mon, 23 Aug 2021 12:14:32 +0200
Subject: [PATCH 13/25] Change board label color according to background

---
 web_src/js/features/projects.js     | 37 ++++++++++++++++++++++++++++-
 web_src/less/features/projects.less | 15 ++++++++++++
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
index f7518f215d1c9..b23667a6b8d51 100644
--- a/web_src/js/features/projects.js
+++ b/web_src/js/features/projects.js
@@ -62,13 +62,16 @@ export default async function initProject() {
   }
 
   $('.edit-project-board').each(function () {
-    const projectTitleLabel = $(this).closest('.board-column-header').find('.board-label');
+    const projectHeader = $(this).closest('.board-column-header');
+    const projectTitleLabel = projectHeader.find('.board-label');
     const projectTitleInput = $(this).find(
       '.content > .form > .field > .project-board-title',
     );
     const projectColorInput = $(this).find('.content > .form > .field > #new_board_color');
     const boardColumn = $(this).closest('.board-column');
 
+    setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
+
     $(this)
       .find('.content > .form > .actions > .red')
       .on('click', function (e) {
@@ -87,6 +90,7 @@ export default async function initProject() {
           projectTitleLabel.text(projectTitleInput.val());
           projectTitleInput.closest('form').removeClass('dirty');
           boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
+          setLabelColor(projectHeader, projectColorInput.val());
           $('.ui.modal').modal('hide');
         });
       });
@@ -147,3 +151,34 @@ export default async function initProject() {
     });
   });
 }
+
+function setLabelColor(label, color) {
+  const red = getRelativeColor(parseInt(color.substr(1, 2), 16));
+  const green = getRelativeColor(parseInt(color.substr(3, 2), 16));
+  const blue = getRelativeColor(parseInt(color.substr(5, 2), 16));
+  const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
+
+  if (luminance > 0.179) {
+    label.removeClass('light-label').addClass('dark-label');
+  } else {
+    label.removeClass('dark-label').addClass('light-label');
+  }
+}
+
+/**
+ * Inspired by W3C recommandation https://www.w3.org/TR/WCAG20/#relativeluminancedef
+ */
+function getRelativeColor(color) {
+  color /= 255;
+  return color <= 0.03928 ? color / 12.92 : ((color + 0.055) / 1.055) ** 2.4;
+}
+
+function rgbToHex(rgb) {
+  rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
+  return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
+}
+
+function hex(x) {
+  const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+  return Number.isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
+}
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index a08207a694d07..d7de2c71d9dcd 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -23,6 +23,21 @@
 .board-column-header {
   display: flex;
   justify-content: space-between;
+
+  &.dark-label {
+    color: #555555 !important;
+
+    .board-label {
+      color: #555555 !important;
+    }
+  }
+  &.light-label {
+    color: #a6aab5 !important;
+
+    .board-label {
+      color: #a6aab5 !important;
+    }
+  }
 }
 
 .board-label {

From 153a7fe82e584a5d680dd01d69f2299cd9923dde Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Mon, 23 Aug 2021 12:21:28 +0200
Subject: [PATCH 14/25] Possibility to remove board color

---
 routers/web/repo/projects.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 9951cb48f08bc..64fdd0e9d8399 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -514,6 +514,8 @@ func EditProjectBoard(ctx *context.Context) {
 
 	if form.Color != "" {
 		board.Color = form.Color
+	} else {
+		board.Color = "#f8f8f8"
 	}
 
 	if form.Sorting != 0 {

From e1ff66a302d8f91d6ad7317ea3529c7020534b3b Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Tue, 31 Aug 2021 08:42:50 +0200
Subject: [PATCH 15/25] Use less variable for project board label color

---
 web_src/less/_base.less             | 2 ++
 web_src/less/features/projects.less | 8 ++++----
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/web_src/less/_base.less b/web_src/less/_base.less
index 8b951b2548d75..65971c64fbb8d 100644
--- a/web_src/less/_base.less
+++ b/web_src/less/_base.less
@@ -114,6 +114,8 @@
   --color-placeholder-text: #aaa;
   --color-editor-line-highlight: var(--color-primary-light-6);
   --color-project-board-bg: var(--color-secondary-light-4);
+  --color-project-board-dark-label: #555555;
+  --color-project-board-light-label: #a6aab5;
   --color-caret: var(--color-text-dark);
   --color-reaction-bg: #0000000a;
   --color-reaction-active-bg: var(--color-primary-alpha-20);
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index d7de2c71d9dcd..eacab5246cab7 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -25,17 +25,17 @@
   justify-content: space-between;
 
   &.dark-label {
-    color: #555555 !important;
+    color: var(--color-project-board-dark-label) !important;
 
     .board-label {
-      color: #555555 !important;
+      color: var(--color-project-board-dark-label) !important;
     }
   }
   &.light-label {
-    color: #a6aab5 !important;
+    color: var(--color-project-board-light-label) !important;
 
     .board-label {
-      color: #a6aab5 !important;
+      color: var(--color-project-board-light-label) !important;
     }
   }
 }

From 7918bb7396a43e2a688e3b2b6aa7c3e115e378bf Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Tue, 7 Sep 2021 21:48:59 +0200
Subject: [PATCH 16/25] Add button to remove board colors

---
 routers/web/repo/projects.go        |  2 --
 templates/repo/projects/view.tmpl   |  7 ++++++-
 web_src/js/features/projects.js     | 23 ++++++++++++++++++++++-
 web_src/less/features/projects.less | 13 +++++++++++++
 4 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 64fdd0e9d8399..9951cb48f08bc 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -514,8 +514,6 @@ func EditProjectBoard(ctx *context.Context) {
 
 	if form.Color != "" {
 		board.Color = form.Color
-	} else {
-		board.Color = "#f8f8f8"
 	}
 
 	if form.Sorting != 0 {
diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index eb893bbcc2492..20349ed48ef2f 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -111,7 +111,12 @@
 
 											<div class="field color-field">
 												<label for="new_board_color">{{$.i18n.Tr "color"}}</label>
-												<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color" name="color" value="{{.Color}}">
+												<div class="ui fluid input">
+													<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color" name="color" value="{{.Color}}">
+													<button class="ui red tiny button delete-board-color" id="delete_board_color" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">
+														{{svg "octicon-trash" 16 "mr-2"}}
+													</button>
+												</div>
 											</div>
 
 											<div class="text right actions">
diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
index b23667a6b8d51..45a02c96803f8 100644
--- a/web_src/js/features/projects.js
+++ b/web_src/js/features/projects.js
@@ -67,7 +67,7 @@ export default async function initProject() {
     const projectTitleInput = $(this).find(
       '.content > .form > .field > .project-board-title',
     );
-    const projectColorInput = $(this).find('.content > .form > .field > #new_board_color');
+    const projectColorInput = $(this).find('.content > .form > .field  #new_board_color');
     const boardColumn = $(this).closest('.board-column');
 
     setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
@@ -150,6 +150,27 @@ export default async function initProject() {
       window.location.reload();
     });
   });
+
+  $(document).on('click', '#delete_board_color', function (e) {    
+    e.preventDefault();
+
+    const projectHeader = $(this).closest('.board-column-header');
+
+    projectHeader.removeClass('light-label').addClass('dark-label');
+
+    $.ajax({
+      url: $(this).data('url'),
+      data: JSON.stringify({color: "#f8f8f8"}),
+      headers: {
+        'X-Csrf-Token': csrf,
+        'X-Remote': true,
+      },
+      contentType: 'application/json',
+      method: 'PUT',
+    }).done(() => {
+      window.location.reload();
+    });
+  });
 }
 
 function setLabelColor(label, color) {
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index eacab5246cab7..8bf5bb65a53c7 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -109,3 +109,16 @@
     top: 10px;
   }
 }
+
+.color-field {
+  .minicolors, .minicolors-input {
+    width: 100% !important;
+  }
+  
+  .delete-board-color {
+      margin-left: -5px;
+      z-index: 4;
+      border-top-left-radius: 0;
+      border-bottom-left-radius: 0;
+  }
+}
\ No newline at end of file

From 20b4dd6e3e5f789df922890c62e77c932e52525a Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Tue, 7 Sep 2021 21:56:40 +0200
Subject: [PATCH 17/25] Add check before update board color

---
 web_src/js/features/projects.js | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
index 45a02c96803f8..cd6eab349f797 100644
--- a/web_src/js/features/projects.js
+++ b/web_src/js/features/projects.js
@@ -70,7 +70,9 @@ export default async function initProject() {
     const projectColorInput = $(this).find('.content > .form > .field  #new_board_color');
     const boardColumn = $(this).closest('.board-column');
 
-    setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
+    if (boardColumn.css('backgroundColor')) {
+      setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
+    }
 
     $(this)
       .find('.content > .form > .actions > .red')
@@ -89,8 +91,10 @@ export default async function initProject() {
         }).done(() => {
           projectTitleLabel.text(projectTitleInput.val());
           projectTitleInput.closest('form').removeClass('dirty');
-          boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
-          setLabelColor(projectHeader, projectColorInput.val());
+          if (projectColorInput.val()) {
+            boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
+            setLabelColor(projectHeader, projectColorInput.val());
+          }
           $('.ui.modal').modal('hide');
         });
       });

From 6a62f90d4190656da2a63fd88bc57a120355bb7b Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Tue, 7 Sep 2021 22:03:14 +0200
Subject: [PATCH 18/25] Linter fix

---
 web_src/js/features/projects.js     |  4 ++--
 web_src/less/features/projects.less | 15 ++++++++-------
 2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
index cd6eab349f797..9800f82087363 100644
--- a/web_src/js/features/projects.js
+++ b/web_src/js/features/projects.js
@@ -155,7 +155,7 @@ export default async function initProject() {
     });
   });
 
-  $(document).on('click', '#delete_board_color', function (e) {    
+  $(document).on('click', '#delete_board_color', function (e) {
     e.preventDefault();
 
     const projectHeader = $(this).closest('.board-column-header');
@@ -164,7 +164,7 @@ export default async function initProject() {
 
     $.ajax({
       url: $(this).data('url'),
-      data: JSON.stringify({color: "#f8f8f8"}),
+      data: JSON.stringify({color: '#f8f8f8'}),
       headers: {
         'X-Csrf-Token': csrf,
         'X-Remote': true,
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index 8bf5bb65a53c7..25d546fae720b 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -111,14 +111,15 @@
 }
 
 .color-field {
-  .minicolors, .minicolors-input {
+  .minicolors,
+  .minicolors-input {
     width: 100% !important;
   }
-  
+
   .delete-board-color {
-      margin-left: -5px;
-      z-index: 4;
-      border-top-left-radius: 0;
-      border-bottom-left-radius: 0;
+    margin-left: -5px;
+    z-index: 4;
+    border-top-left-radius: 0;
+    border-bottom-left-radius: 0;
   }
-}
\ No newline at end of file
+}

From 1c98c83e94bae11d6aa50bcf0fa0ca2f20ba6f89 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Thu, 9 Sep 2021 13:09:50 +0200
Subject: [PATCH 19/25] Code review

---
 models/project_board.go      | 2 +-
 routers/web/repo/projects.go | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/models/project_board.go b/models/project_board.go
index ca6a3edcdedb4..13b9fb9693fb4 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -186,7 +186,7 @@ func updateProjectBoard(e Engine, board *ProjectBoard) error {
 		fieldToUpdate = append(fieldToUpdate, "title")
 	}
 
-	if board.Color != "" {
+	if len(board.Color) != 0 {
 		if !BoardColorPattern.MatchString(board.Color) {
 			return fmt.Errorf("bad color code: %s", board.Color)
 		}
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 9951cb48f08bc..e13faeb02ff5c 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -512,7 +512,7 @@ func EditProjectBoard(ctx *context.Context) {
 		board.Title = form.Title
 	}
 
-	if form.Color != "" {
+	if len(form.Color) != 0 {
 		board.Color = form.Color
 	}
 

From a86ee3cc93e088964efdf5d53dc9564e1affdc08 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Thu, 9 Sep 2021 13:48:56 +0200
Subject: [PATCH 20/25] Remove btn which remove board color and add precolor

---
 models/project_board.go             |  2 +-
 routers/web/repo/projects.go        |  2 ++
 templates/repo/projects/view.tmpl   |  8 ++++----
 web_src/js/features/projects.js     | 25 ++-----------------------
 web_src/less/_base.less             | 13 +++++++++++++
 web_src/less/_repository.less       | 13 -------------
 web_src/less/features/projects.less | 18 +++++++-----------
 7 files changed, 29 insertions(+), 52 deletions(-)

diff --git a/models/project_board.go b/models/project_board.go
index 13b9fb9693fb4..dcf55f4c5e269 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -190,8 +190,8 @@ func updateProjectBoard(e Engine, board *ProjectBoard) error {
 		if !BoardColorPattern.MatchString(board.Color) {
 			return fmt.Errorf("bad color code: %s", board.Color)
 		}
-		fieldToUpdate = append(fieldToUpdate, "color")
 	}
+	fieldToUpdate = append(fieldToUpdate, "color")
 
 	_, err := e.ID(board.ID).Cols(fieldToUpdate...).Update(board)
 
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index c493abda75623..1422d75bd3cbf 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -516,6 +516,8 @@ func EditProjectBoard(ctx *context.Context) {
 
 	if len(form.Color) != 0 {
 		board.Color = form.Color
+	} else {
+		board.Color = ""
 	}
 
 	if form.Sorting != 0 {
diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index 20349ed48ef2f..a94696169f831 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -111,11 +111,11 @@
 
 											<div class="field color-field">
 												<label for="new_board_color">{{$.i18n.Tr "color"}}</label>
-												<div class="ui fluid input">
+												<div class="color picker column">
 													<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color" name="color" value="{{.Color}}">
-													<button class="ui red tiny button delete-board-color" id="delete_board_color" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}">
-														{{svg "octicon-trash" 16 "mr-2"}}
-													</button>
+													<div class="column precolors">
+														{{template "repo/issue/label_precolors"}}
+													</div>
 												</div>
 											</div>
 
diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
index 9800f82087363..c02b81d1c6358 100644
--- a/web_src/js/features/projects.js
+++ b/web_src/js/features/projects.js
@@ -23,7 +23,7 @@ export default async function initProject() {
           if (parseInt($(column).data('sorting')) !== i) {
             $.ajax({
               url: $(column).data('url'),
-              data: JSON.stringify({sorting: i}),
+              data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}),
               headers: {
                 'X-Csrf-Token': csrf,
                 'X-Remote': true,
@@ -92,9 +92,9 @@ export default async function initProject() {
           projectTitleLabel.text(projectTitleInput.val());
           projectTitleInput.closest('form').removeClass('dirty');
           if (projectColorInput.val()) {
-            boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
             setLabelColor(projectHeader, projectColorInput.val());
           }
+          boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
           $('.ui.modal').modal('hide');
         });
       });
@@ -154,27 +154,6 @@ export default async function initProject() {
       window.location.reload();
     });
   });
-
-  $(document).on('click', '#delete_board_color', function (e) {
-    e.preventDefault();
-
-    const projectHeader = $(this).closest('.board-column-header');
-
-    projectHeader.removeClass('light-label').addClass('dark-label');
-
-    $.ajax({
-      url: $(this).data('url'),
-      data: JSON.stringify({color: '#f8f8f8'}),
-      headers: {
-        'X-Csrf-Token': csrf,
-        'X-Remote': true,
-      },
-      contentType: 'application/json',
-      method: 'PUT',
-    }).done(() => {
-      window.location.reload();
-    });
-  });
 }
 
 function setLabelColor(label, color) {
diff --git a/web_src/less/_base.less b/web_src/less/_base.less
index 65971c64fbb8d..e338f5d6692fa 100644
--- a/web_src/less/_base.less
+++ b/web_src/less/_base.less
@@ -2086,3 +2086,16 @@ table th[data-sortt-desc] {
   margin-top: -.5em;
   margin-bottom: -.5em;
 }
+
+.precolors {
+  padding-left: 0;
+  padding-right: 0;
+  margin: 3px 10px auto;
+  width: 120px;
+
+  .color {
+    float: left;
+    width: 15px;
+    height: 15px;
+  }
+}
diff --git a/web_src/less/_repository.less b/web_src/less/_repository.less
index a58fe3698d2ec..81962e8e02af4 100644
--- a/web_src/less/_repository.less
+++ b/web_src/less/_repository.less
@@ -2692,19 +2692,6 @@
       width: 15px;
       height: 15px;
     }
-
-    .precolors {
-      padding-left: 0;
-      padding-right: 0;
-      margin: 3px 10px auto;
-      width: 120px;
-
-      .color {
-        float: left;
-        width: 15px;
-        height: 15px;
-      }
-    }
   }
 }
 
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index 25d546fae720b..2dc9b96d87fa6 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -110,16 +110,12 @@
   }
 }
 
-.color-field {
-  .minicolors,
-  .minicolors-input {
-    width: 100% !important;
-  }
-
-  .delete-board-color {
-    margin-left: -5px;
-    z-index: 4;
-    border-top-left-radius: 0;
-    border-bottom-left-radius: 0;
+.edit-project-board {
+  .color.picker.column {
+    display: flex;
+    
+    .minicolors {
+      flex: 1;
+    }
   }
 }

From 5e335dc4b42bc40e7d32bcf713687eca94bd9aa9 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Thu, 9 Sep 2021 13:51:32 +0200
Subject: [PATCH 21/25] linter fix

---
 web_src/less/features/projects.less | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index 2dc9b96d87fa6..769ee14dd432a 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -113,7 +113,7 @@
 .edit-project-board {
   .color.picker.column {
     display: flex;
-    
+
     .minicolors {
       flex: 1;
     }

From c39ca476f35b19f88e6da53aa68555022bcfe414 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Thu, 9 Sep 2021 13:59:46 +0200
Subject: [PATCH 22/25] Add precolor on new project board form

---
 templates/repo/projects/view.tmpl   | 9 +++++++--
 web_src/less/features/projects.less | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl
index a94696169f831..81af12becefff 100644
--- a/templates/repo/projects/view.tmpl
+++ b/templates/repo/projects/view.tmpl
@@ -10,7 +10,7 @@
 				{{if and .CanWriteProjects (not .Repository.IsArchived) .PageIsProjects}}
 					<a class="ui green button show-modal item" data-modal="#new-board-item">{{.i18n.Tr "new_project_board"}}</a>
 				{{end}}
-				<div class="ui small modal" id="new-board-item">
+				<div class="ui small modal new-board-modal" id="new-board-item">
 					<div class="header">
 						{{$.i18n.Tr "repo.projects.board.new"}}
 					</div>
@@ -23,7 +23,12 @@
 
 							<div class="field color-field">
 								<label for="new_board_color">{{$.i18n.Tr "color"}}</label>
-								<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color_picker" name="color">
+								<div class="color picker column">
+									<input class="color-picker" maxlength="7" placeholder="#c320f6" id="new_board_color_picker" name="color">
+									<div class="column precolors">
+										{{template "repo/issue/label_precolors"}}
+									</div>
+								</div>
 							</div>
 
 							<div class="text right actions">
diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index 769ee14dd432a..e69b72f550992 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -110,7 +110,7 @@
   }
 }
 
-.edit-project-board {
+.edit-project-board, .new-board-modal {
   .color.picker.column {
     display: flex;
 

From 694bc41f18b587064f214be1cce56a56fd5a4827 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Thu, 9 Sep 2021 14:01:02 +0200
Subject: [PATCH 23/25] linter fix

---
 web_src/less/features/projects.less | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/web_src/less/features/projects.less b/web_src/less/features/projects.less
index e69b72f550992..99722b6cc5474 100644
--- a/web_src/less/features/projects.less
+++ b/web_src/less/features/projects.less
@@ -110,7 +110,8 @@
   }
 }
 
-.edit-project-board, .new-board-modal {
+.edit-project-board,
+.new-board-modal {
   .color.picker.column {
     display: flex;
 

From d769196ab3ad749a061c78f45247c648d93ffc57 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Fri, 24 Sep 2021 22:36:01 +0200
Subject: [PATCH 24/25] Board color - Code review

---
 models/project_board.go      | 6 ++----
 routers/web/repo/projects.go | 6 +-----
 2 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/models/project_board.go b/models/project_board.go
index dcf55f4c5e269..ec613b230877b 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -186,10 +186,8 @@ func updateProjectBoard(e Engine, board *ProjectBoard) error {
 		fieldToUpdate = append(fieldToUpdate, "title")
 	}
 
-	if len(board.Color) != 0 {
-		if !BoardColorPattern.MatchString(board.Color) {
-			return fmt.Errorf("bad color code: %s", board.Color)
-		}
+	if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) {
+		return fmt.Errorf("bad color code: %s", board.Color)
 	}
 	fieldToUpdate = append(fieldToUpdate, "color")
 
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 1422d75bd3cbf..2490efc92319a 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -514,11 +514,7 @@ func EditProjectBoard(ctx *context.Context) {
 		board.Title = form.Title
 	}
 
-	if len(form.Color) != 0 {
-		board.Color = form.Color
-	} else {
-		board.Color = ""
-	}
+	board.Color = form.Color
 
 	if form.Sorting != 0 {
 		board.Sorting = form.Sorting

From 3b12e74e24c07c039a1e0f2ce778fc722854f2b0 Mon Sep 17 00:00:00 2001
From: Romain <romain@duminil.eu>
Date: Mon, 27 Sep 2021 09:54:08 +0200
Subject: [PATCH 25/25] Feature Board colored - Code review

---
 models/project_board.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/models/project_board.go b/models/project_board.go
index 9126825a3a3c0..e6c9f0338632b 100644
--- a/models/project_board.go
+++ b/models/project_board.go
@@ -107,7 +107,7 @@ func createBoardsForProjectsType(sess *xorm.Session, project *Project) error {
 
 // NewProjectBoard adds a new project board to a given project
 func NewProjectBoard(board *ProjectBoard) error {
-	if board.Color != "" && !BoardColorPattern.MatchString(board.Color) {
+	if len(board.Color) != 0 && !BoardColorPattern.MatchString(board.Color) {
 		return fmt.Errorf("bad color code: %s", board.Color)
 	}