mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-09-03 16:55:36 +02:00
Preview images for Issue cards in Project Board view (#22112)
Original Issue: https://github.com/go-gitea/gitea/issues/22102 This addition would be a big benefit for design and art teams using the issue tracking. The preview will be the latest "image type" attachments on an issue- simple, and allows for automatic updates of the cover image as issue progress is made! This would make Gitea competitive with Trello... wouldn't it be amazing to say goodbye to Atlassian products? Ha. First image is the most recent, the SQL will fetch up to 5 latest images (URL string). All images supported by browsers plus upcoming formats: *.avif *.bmp *.gif *.jpg *.jpeg *.jxl *.png *.svg *.webp The CSS will try to center-align images until it cannot, then it will left align with overflow hidden. Single images get to be slightly larger! Tested so far on: Chrome, Firefox, Android Chrome, Android Firefox. Current revision with light and dark themes: ![image](https://user-images.githubusercontent.com/24665/207066878-58e6bf73-0c93-4caa-8d40-38f4432b3578.png) ![image](https://user-images.githubusercontent.com/24665/207066555-293f65c3-e706-4888-8516-de8ec632d638.png) --------- Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
parent
e9288c2477
commit
fb1a2a13f0
15 changed files with 175 additions and 19 deletions
|
@ -455,6 +455,8 @@ var migrations = []Migration{
|
||||||
NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens),
|
NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens),
|
||||||
// v240 -> v241
|
// v240 -> v241
|
||||||
NewMigration("Add actions tables", v1_19.AddActionsTables),
|
NewMigration("Add actions tables", v1_19.AddActionsTables),
|
||||||
|
// v241 -> v242
|
||||||
|
NewMigration("Add card_type column to project table", v1_19.AddCardTypeToProjectTable),
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentDBVersion returns the current db version
|
// GetCurrentDBVersion returns the current db version
|
||||||
|
|
17
models/migrations/v1_19/v241.go
Normal file
17
models/migrations/v1_19/v241.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v1_19 //nolint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddCardTypeToProjectTable: add CardType column, setting existing rows to CardTypeTextOnly
|
||||||
|
func AddCardTypeToProjectTable(x *xorm.Engine) error {
|
||||||
|
type Project struct {
|
||||||
|
CardType int `xorm:"NOT NULL"`
|
||||||
|
}
|
||||||
|
|
||||||
|
return x.Sync(new(Project))
|
||||||
|
}
|
|
@ -19,6 +19,9 @@ type (
|
||||||
// BoardType is used to represent a project board type
|
// BoardType is used to represent a project board type
|
||||||
BoardType uint8
|
BoardType uint8
|
||||||
|
|
||||||
|
// CardType is used to represent a project board card type
|
||||||
|
CardType uint8
|
||||||
|
|
||||||
// BoardList is a list of all project boards in a repository
|
// BoardList is a list of all project boards in a repository
|
||||||
BoardList []*Board
|
BoardList []*Board
|
||||||
)
|
)
|
||||||
|
@ -34,6 +37,14 @@ const (
|
||||||
BoardTypeBugTriage
|
BoardTypeBugTriage
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CardTypeTextOnly is a project board card type that is text only
|
||||||
|
CardTypeTextOnly CardType = iota
|
||||||
|
|
||||||
|
// CardTypeImagesAndText is a project board card type that has images and text
|
||||||
|
CardTypeImagesAndText
|
||||||
|
)
|
||||||
|
|
||||||
// BoardColorPattern is a regexp witch can validate BoardColor
|
// BoardColorPattern is a regexp witch can validate BoardColor
|
||||||
var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
|
var BoardColorPattern = regexp.MustCompile("^#[0-9a-fA-F]{6}$")
|
||||||
|
|
||||||
|
@ -85,6 +96,16 @@ func IsBoardTypeValid(p BoardType) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsCardTypeValid checks if the project board card type is valid
|
||||||
|
func IsCardTypeValid(p CardType) bool {
|
||||||
|
switch p {
|
||||||
|
case CardTypeTextOnly, CardTypeImagesAndText:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createBoardsForProjectsType(ctx context.Context, project *Project) error {
|
func createBoardsForProjectsType(ctx context.Context, project *Project) error {
|
||||||
var items []string
|
var items []string
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// ProjectsConfig is used to identify the type of board that is being created
|
// BoardConfig is used to identify the type of board that is being created
|
||||||
ProjectsConfig struct {
|
BoardConfig struct {
|
||||||
BoardType BoardType
|
BoardType BoardType
|
||||||
Translation string
|
Translation string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CardConfig is used to identify the type of board card that is being used
|
||||||
|
CardConfig struct {
|
||||||
|
CardType CardType
|
||||||
|
Translation string
|
||||||
|
}
|
||||||
|
|
||||||
// Type is used to identify the type of project in question and ownership
|
// Type is used to identify the type of project in question and ownership
|
||||||
Type uint8
|
Type uint8
|
||||||
)
|
)
|
||||||
|
@ -91,6 +97,7 @@ type Project struct {
|
||||||
CreatorID int64 `xorm:"NOT NULL"`
|
CreatorID int64 `xorm:"NOT NULL"`
|
||||||
IsClosed bool `xorm:"INDEX"`
|
IsClosed bool `xorm:"INDEX"`
|
||||||
BoardType BoardType
|
BoardType BoardType
|
||||||
|
CardType CardType
|
||||||
Type Type
|
Type Type
|
||||||
|
|
||||||
RenderedContent string `xorm:"-"`
|
RenderedContent string `xorm:"-"`
|
||||||
|
@ -145,15 +152,23 @@ func init() {
|
||||||
db.RegisterModel(new(Project))
|
db.RegisterModel(new(Project))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProjectsConfig retrieves the types of configurations projects could have
|
// GetBoardConfig retrieves the types of configurations project boards could have
|
||||||
func GetProjectsConfig() []ProjectsConfig {
|
func GetBoardConfig() []BoardConfig {
|
||||||
return []ProjectsConfig{
|
return []BoardConfig{
|
||||||
{BoardTypeNone, "repo.projects.type.none"},
|
{BoardTypeNone, "repo.projects.type.none"},
|
||||||
{BoardTypeBasicKanban, "repo.projects.type.basic_kanban"},
|
{BoardTypeBasicKanban, "repo.projects.type.basic_kanban"},
|
||||||
{BoardTypeBugTriage, "repo.projects.type.bug_triage"},
|
{BoardTypeBugTriage, "repo.projects.type.bug_triage"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCardConfig retrieves the types of configurations project board cards could have
|
||||||
|
func GetCardConfig() []CardConfig {
|
||||||
|
return []CardConfig{
|
||||||
|
{CardTypeTextOnly, "repo.projects.card_type.text_only"},
|
||||||
|
{CardTypeImagesAndText, "repo.projects.card_type.images_and_text"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// IsTypeValid checks if a project type is valid
|
// IsTypeValid checks if a project type is valid
|
||||||
func IsTypeValid(p Type) bool {
|
func IsTypeValid(p Type) bool {
|
||||||
switch p {
|
switch p {
|
||||||
|
@ -237,6 +252,10 @@ func NewProject(p *Project) error {
|
||||||
p.BoardType = BoardTypeNone
|
p.BoardType = BoardTypeNone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !IsCardTypeValid(p.CardType) {
|
||||||
|
p.CardType = CardTypeTextOnly
|
||||||
|
}
|
||||||
|
|
||||||
if !IsTypeValid(p.Type) {
|
if !IsTypeValid(p.Type) {
|
||||||
return util.NewInvalidArgumentErrorf("project type is not valid")
|
return util.NewInvalidArgumentErrorf("project type is not valid")
|
||||||
}
|
}
|
||||||
|
@ -280,9 +299,14 @@ func GetProjectByID(ctx context.Context, id int64) (*Project, error) {
|
||||||
|
|
||||||
// UpdateProject updates project properties
|
// UpdateProject updates project properties
|
||||||
func UpdateProject(ctx context.Context, p *Project) error {
|
func UpdateProject(ctx context.Context, p *Project) error {
|
||||||
|
if !IsCardTypeValid(p.CardType) {
|
||||||
|
p.CardType = CardTypeTextOnly
|
||||||
|
}
|
||||||
|
|
||||||
_, err := db.GetEngine(ctx).ID(p.ID).Cols(
|
_, err := db.GetEngine(ctx).ID(p.ID).Cols(
|
||||||
"title",
|
"title",
|
||||||
"description",
|
"description",
|
||||||
|
"card_type",
|
||||||
).Update(p)
|
).Update(p)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ func TestProject(t *testing.T) {
|
||||||
project := &Project{
|
project := &Project{
|
||||||
Type: TypeRepository,
|
Type: TypeRepository,
|
||||||
BoardType: BoardTypeBasicKanban,
|
BoardType: BoardTypeBasicKanban,
|
||||||
|
CardType: CardTypeTextOnly,
|
||||||
Title: "New Project",
|
Title: "New Project",
|
||||||
RepoID: 1,
|
RepoID: 1,
|
||||||
CreatedUnix: timeutil.TimeStampNow(),
|
CreatedUnix: timeutil.TimeStampNow(),
|
||||||
|
|
|
@ -132,6 +132,21 @@ func GetAttachmentsByIssueID(ctx context.Context, issueID int64) ([]*Attachment,
|
||||||
return attachments, db.GetEngine(ctx).Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
|
return attachments, db.GetEngine(ctx).Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAttachmentsByIssueIDImagesLatest returns the latest image attachments of an issue.
|
||||||
|
func GetAttachmentsByIssueIDImagesLatest(ctx context.Context, issueID int64) ([]*Attachment, error) {
|
||||||
|
attachments := make([]*Attachment, 0, 5)
|
||||||
|
return attachments, db.GetEngine(ctx).Where(`issue_id = ? AND (name like '%.apng'
|
||||||
|
OR name like '%.avif'
|
||||||
|
OR name like '%.bmp'
|
||||||
|
OR name like '%.gif'
|
||||||
|
OR name like '%.jpg'
|
||||||
|
OR name like '%.jpeg'
|
||||||
|
OR name like '%.jxl'
|
||||||
|
OR name like '%.png'
|
||||||
|
OR name like '%.svg'
|
||||||
|
OR name like '%.webp')`, issueID).Desc("comment_id").Limit(5).Find(&attachments)
|
||||||
|
}
|
||||||
|
|
||||||
// GetAttachmentsByCommentID returns all attachments if comment by given ID.
|
// GetAttachmentsByCommentID returns all attachments if comment by given ID.
|
||||||
func GetAttachmentsByCommentID(ctx context.Context, commentID int64) ([]*Attachment, error) {
|
func GetAttachmentsByCommentID(ctx context.Context, commentID int64) ([]*Attachment, error) {
|
||||||
attachments := make([]*Attachment, 0, 10)
|
attachments := make([]*Attachment, 0, 10)
|
||||||
|
|
|
@ -1231,6 +1231,9 @@ projects.board.color = "Color"
|
||||||
projects.open = Open
|
projects.open = Open
|
||||||
projects.close = Close
|
projects.close = Close
|
||||||
projects.board.assigned_to = Assigned to
|
projects.board.assigned_to = Assigned to
|
||||||
|
projects.card_type.desc = "Card Previews"
|
||||||
|
projects.card_type.images_and_text = "Images and Text"
|
||||||
|
projects.card_type.text_only = "Text Only"
|
||||||
|
|
||||||
issues.desc = Organize bug reports, tasks and milestones.
|
issues.desc = Organize bug reports, tasks and milestones.
|
||||||
issues.filter_assignees = Filter Assignee
|
issues.filter_assignees = Filter Assignee
|
||||||
|
|
|
@ -121,7 +121,7 @@ func canWriteUnit(ctx *context.Context) bool {
|
||||||
// NewProject render creating a project page
|
// NewProject render creating a project page
|
||||||
func NewProject(ctx *context.Context) {
|
func NewProject(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
|
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
|
||||||
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
|
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
|
||||||
ctx.Data["CanWriteProjects"] = canWriteUnit(ctx)
|
ctx.Data["CanWriteProjects"] = canWriteUnit(ctx)
|
||||||
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
|
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
|
||||||
shared_user.RenderUserHeader(ctx)
|
shared_user.RenderUserHeader(ctx)
|
||||||
|
@ -137,7 +137,7 @@ func NewProjectPost(ctx *context.Context) {
|
||||||
if ctx.HasError() {
|
if ctx.HasError() {
|
||||||
ctx.Data["CanWriteProjects"] = canWriteUnit(ctx)
|
ctx.Data["CanWriteProjects"] = canWriteUnit(ctx)
|
||||||
ctx.Data["PageIsViewProjects"] = true
|
ctx.Data["PageIsViewProjects"] = true
|
||||||
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
|
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
|
||||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
issues_model "code.gitea.io/gitea/models/issues"
|
issues_model "code.gitea.io/gitea/models/issues"
|
||||||
"code.gitea.io/gitea/models/perm"
|
"code.gitea.io/gitea/models/perm"
|
||||||
project_model "code.gitea.io/gitea/models/project"
|
project_model "code.gitea.io/gitea/models/project"
|
||||||
|
attachment_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
@ -123,7 +124,8 @@ func Projects(ctx *context.Context) {
|
||||||
// NewProject render creating a project page
|
// NewProject render creating a project page
|
||||||
func NewProject(ctx *context.Context) {
|
func NewProject(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
|
ctx.Data["Title"] = ctx.Tr("repo.projects.new")
|
||||||
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
|
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
|
||||||
|
ctx.Data["CardTypes"] = project_model.GetCardConfig()
|
||||||
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
||||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||||
}
|
}
|
||||||
|
@ -135,7 +137,8 @@ func NewProjectPost(ctx *context.Context) {
|
||||||
|
|
||||||
if ctx.HasError() {
|
if ctx.HasError() {
|
||||||
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
||||||
ctx.Data["ProjectTypes"] = project_model.GetProjectsConfig()
|
ctx.Data["BoardTypes"] = project_model.GetBoardConfig()
|
||||||
|
ctx.Data["CardTypes"] = project_model.GetCardConfig()
|
||||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -146,6 +149,7 @@ func NewProjectPost(ctx *context.Context) {
|
||||||
Description: form.Content,
|
Description: form.Content,
|
||||||
CreatorID: ctx.Doer.ID,
|
CreatorID: ctx.Doer.ID,
|
||||||
BoardType: form.BoardType,
|
BoardType: form.BoardType,
|
||||||
|
CardType: form.CardType,
|
||||||
Type: project_model.TypeRepository,
|
Type: project_model.TypeRepository,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
ctx.ServerError("NewProject", err)
|
ctx.ServerError("NewProject", err)
|
||||||
|
@ -212,6 +216,7 @@ func EditProject(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
|
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
|
||||||
ctx.Data["PageIsEditProjects"] = true
|
ctx.Data["PageIsEditProjects"] = true
|
||||||
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
||||||
|
ctx.Data["CardTypes"] = project_model.GetCardConfig()
|
||||||
|
|
||||||
p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -229,6 +234,7 @@ func EditProject(ctx *context.Context) {
|
||||||
|
|
||||||
ctx.Data["title"] = p.Title
|
ctx.Data["title"] = p.Title
|
||||||
ctx.Data["content"] = p.Description
|
ctx.Data["content"] = p.Description
|
||||||
|
ctx.Data["card_type"] = p.CardType
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||||
}
|
}
|
||||||
|
@ -239,6 +245,7 @@ func EditProjectPost(ctx *context.Context) {
|
||||||
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
|
ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
|
||||||
ctx.Data["PageIsEditProjects"] = true
|
ctx.Data["PageIsEditProjects"] = true
|
||||||
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
|
||||||
|
ctx.Data["CardTypes"] = project_model.GetCardConfig()
|
||||||
|
|
||||||
if ctx.HasError() {
|
if ctx.HasError() {
|
||||||
ctx.HTML(http.StatusOK, tplProjectsNew)
|
ctx.HTML(http.StatusOK, tplProjectsNew)
|
||||||
|
@ -261,6 +268,7 @@ func EditProjectPost(ctx *context.Context) {
|
||||||
|
|
||||||
p.Title = form.Title
|
p.Title = form.Title
|
||||||
p.Description = form.Content
|
p.Description = form.Content
|
||||||
|
p.CardType = form.CardType
|
||||||
if err = project_model.UpdateProject(ctx, p); err != nil {
|
if err = project_model.UpdateProject(ctx, p); err != nil {
|
||||||
ctx.ServerError("UpdateProjects", err)
|
ctx.ServerError("UpdateProjects", err)
|
||||||
return
|
return
|
||||||
|
@ -302,6 +310,18 @@ func ViewProject(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if project.CardType != project_model.CardTypeTextOnly {
|
||||||
|
issuesAttachmentMap := make(map[int64][]*attachment_model.Attachment)
|
||||||
|
for _, issuesList := range issuesMap {
|
||||||
|
for _, issue := range issuesList {
|
||||||
|
if issueAttachment, err := attachment_model.GetAttachmentsByIssueIDImagesLatest(ctx, issue.ID); err == nil {
|
||||||
|
issuesAttachmentMap[issue.ID] = issueAttachment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.Data["issuesAttachmentMap"] = issuesAttachmentMap
|
||||||
|
}
|
||||||
|
|
||||||
linkedPrsMap := make(map[int64][]*issues_model.Issue)
|
linkedPrsMap := make(map[int64][]*issues_model.Issue)
|
||||||
for _, issuesList := range issuesMap {
|
for _, issuesList := range issuesMap {
|
||||||
for _, issue := range issuesList {
|
for _, issue := range issuesList {
|
||||||
|
|
|
@ -512,6 +512,7 @@ type CreateProjectForm struct {
|
||||||
Title string `binding:"Required;MaxSize(100)"`
|
Title string `binding:"Required;MaxSize(100)"`
|
||||||
Content string
|
Content string
|
||||||
BoardType project_model.BoardType
|
BoardType project_model.BoardType
|
||||||
|
CardType project_model.CardType
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserCreateProjectForm is a from for creating an individual or organization
|
// UserCreateProjectForm is a from for creating an individual or organization
|
||||||
|
@ -520,6 +521,7 @@ type UserCreateProjectForm struct {
|
||||||
Title string `binding:"Required;MaxSize(100)"`
|
Title string `binding:"Required;MaxSize(100)"`
|
||||||
Content string
|
Content string
|
||||||
BoardType project_model.BoardType
|
BoardType project_model.BoardType
|
||||||
|
CardType project_model.CardType
|
||||||
UID int64 `binding:"Required"`
|
UID int64 `binding:"Required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<input type="hidden" name="board_type" value="{{.type}}">
|
<input type="hidden" name="board_type" value="{{.type}}">
|
||||||
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
|
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
{{range $element := .ProjectTypes}}
|
{{range $element := .BoardTypes}}
|
||||||
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
|
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,17 +34,38 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if not .PageIsEditProjects}}
|
{{if not .PageIsEditProjects}}
|
||||||
<label>{{.locale.Tr "repo.projects.template.desc"}}</label>
|
<div class="field">
|
||||||
<div class="ui selection dropdown">
|
<label>{{.locale.Tr "repo.projects.template.desc"}}</label>
|
||||||
<input type="hidden" name="board_type" value="{{.type}}">
|
<div class="ui selection dropdown">
|
||||||
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
|
<input type="hidden" name="board_type" value="{{.type}}">
|
||||||
<div class="menu">
|
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
|
||||||
{{range $element := .ProjectTypes}}
|
<div class="menu">
|
||||||
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
|
{{range $element := .BoardTypes}}
|
||||||
{{end}}
|
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label>{{.locale.Tr "repo.projects.card_type.desc"}}</label>
|
||||||
|
<div class="ui selection dropdown">
|
||||||
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
|
{{range $element := .CardTypes}}
|
||||||
|
{{if or (eq $.card_type $element.CardType) (and (not $.card_type) (eq $element.CardType 2))}}
|
||||||
|
<input type="hidden" name="card_type" value="{{$element.CardType}}">
|
||||||
|
<div class="default text">{{$.locale.Tr $element.Translation}}</div>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
<div class="menu">
|
||||||
|
{{range $element := .CardTypes}}
|
||||||
|
<div class="item" data-id="{{$element.CardType}}" data-value="{{$element.CardType}}">{{$.locale.Tr $element.Translation}}</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
|
|
|
@ -179,6 +179,13 @@
|
||||||
|
|
||||||
<!-- start issue card -->
|
<!-- start issue card -->
|
||||||
<div class="card board-card" data-issue="{{.ID}}">
|
<div class="card board-card" data-issue="{{.ID}}">
|
||||||
|
{{if eq $.Project.CardType 1}}{{/* Images and Text*/}}
|
||||||
|
<div class="card-attachment-images">
|
||||||
|
{{range (index $.issuesAttachmentMap .ID)}}
|
||||||
|
<img src="{{.DownloadURL}}" alt="{{.Name}}" />
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
<div class="content p-0">
|
<div class="content p-0">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<span class="dif ac vm {{if .IsClosed}}red{{else}}green{{end}}">
|
<span class="dif ac vm {{if .IsClosed}}red{{else}}green{{end}}">
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
<input type="hidden" name="board_type" value="{{.type}}">
|
<input type="hidden" name="board_type" value="{{.type}}">
|
||||||
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
|
<div class="default text">{{.locale.Tr "repo.projects.template.desc_helper"}}</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
{{range $element := .ProjectTypes}}
|
{{range $element := .BoardTypes}}
|
||||||
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
|
<div class="item" data-id="{{$element.BoardType}}" data-value="{{$element.BoardType}}">{{$.locale.Tr $element.Translation}}</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -72,6 +72,10 @@
|
||||||
margin-right: auto !important;
|
margin-right: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-column .ui.cards > .card > .content {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
.board-card {
|
.board-card {
|
||||||
margin: 4px 2px !important;
|
margin: 4px 2px !important;
|
||||||
border-radius: 5px !important;
|
border-radius: 5px !important;
|
||||||
|
@ -90,6 +94,25 @@
|
||||||
font-size: 16px !important;
|
font-size: 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.board-card .card-attachment-images {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-card .card-attachment-images img {
|
||||||
|
display: inline-block;
|
||||||
|
max-height: 50px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-card .card-attachment-images img:only-child {
|
||||||
|
max-height: 90px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.card-ghost {
|
.card-ghost {
|
||||||
border-style: dashed !important;
|
border-style: dashed !important;
|
||||||
background: none !important;
|
background: none !important;
|
||||||
|
|
Loading…
Reference in a new issue