]> source.dussan.org Git - gitea.git/commitdiff
Fix milestone deadline and date related problems (#32339)
authorLunny Xiao <xiaolunwen@gmail.com>
Tue, 5 Nov 2024 07:46:40 +0000 (23:46 -0800)
committerGitHub <noreply@github.com>
Tue, 5 Nov 2024 07:46:40 +0000 (07:46 +0000)
Use zero instead of 9999-12-31 for deadline
Fix #32291

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
23 files changed:
models/actions/schedule_spec_test.go
models/issues/milestone.go
models/migrations/migrations.go
models/migrations/v1_23/v307.go [new file with mode: 0644]
modules/gitgraph/graph.go
modules/gitgraph/graph_models.go
modules/templates/util_date.go
routers/api/v1/repo/issue.go
routers/api/v1/repo/milestone.go
routers/common/deadline.go [new file with mode: 0644]
routers/web/repo/issue.go
routers/web/repo/milestone.go
routers/web/web.go
services/convert/issue.go
templates/repo/graph/commits.tmpl
templates/repo/issue/milestone_new.tmpl
templates/repo/issue/view_content/sidebar.tmpl
tests/integration/api_issue_milestone_test.go
tests/integration/issue_test.go
web_src/js/features/repo-issue-sidebar.ts
web_src/js/features/repo-issue.ts
web_src/js/features/repo-milestone.ts
web_src/js/index.ts

index 0c26fce4b2c5ea7e92987735e04bc14d2164eb35..57221461dfec578ca59180f2beedb7217c17e0e5 100644 (file)
@@ -7,19 +7,17 @@ import (
        "testing"
        "time"
 
+       "code.gitea.io/gitea/modules/test"
+
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
 )
 
 func TestActionScheduleSpec_Parse(t *testing.T) {
        // Mock the local timezone is not UTC
-       local := time.Local
        tz, err := time.LoadLocation("Asia/Shanghai")
        require.NoError(t, err)
-       defer func() {
-               time.Local = local
-       }()
-       time.Local = tz
+       defer test.MockVariableValue(&time.Local, tz)()
 
        now, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
        require.NoError(t, err)
index db0312adf005711a4677bf522ea548728099a70f..4c9bae58f7d40f1b3ef5025d21fbf37c37867a61 100644 (file)
@@ -84,10 +84,9 @@ func (m *Milestone) BeforeUpdate() {
 // this object.
 func (m *Milestone) AfterLoad() {
        m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
-       if m.DeadlineUnix.Year() == 9999 {
+       if m.DeadlineUnix == 0 {
                return
        }
-
        m.DeadlineString = m.DeadlineUnix.FormatDate()
        if m.IsClosed {
                m.IsOverdue = m.ClosedDateUnix >= m.DeadlineUnix
index ddf20d9542cfe19aec6e16fc05b36bfa2585404e..41f337b838ad6826366728eccb43abe31722b0c1 100644 (file)
@@ -364,6 +364,7 @@ func prepareMigrationTasks() []*migration {
                newMigration(304, "Add index for release sha1", v1_23.AddIndexForReleaseSha1),
                newMigration(305, "Add Repository Licenses", v1_23.AddRepositoryLicenses),
                newMigration(306, "Add BlockAdminMergeOverride to ProtectedBranch", v1_23.AddBlockAdminMergeOverrideBranchProtection),
+               newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate),
        }
        return preparedMigrations
 }
diff --git a/models/migrations/v1_23/v307.go b/models/migrations/v1_23/v307.go
new file mode 100644 (file)
index 0000000..ef7f5f2
--- /dev/null
@@ -0,0 +1,21 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package v1_23 //nolint
+
+import (
+       "code.gitea.io/gitea/modules/timeutil"
+
+       "xorm.io/xorm"
+)
+
+func FixMilestoneNoDueDate(x *xorm.Engine) error {
+       type Milestone struct {
+               DeadlineUnix timeutil.TimeStamp
+       }
+       // Wednesday, December 1, 9999 12:00:00 AM GMT+00:00
+       _, err := x.Table("milestone").Where("deadline_unix > 253399622400").
+               Cols("deadline_unix").
+               Update(&Milestone{DeadlineUnix: 0})
+       return err
+}
index 331ad6b218de92754c196b9609c485a5ac456da5..7e12be030fb351b23ee3f725bc7a0d7a9adba610 100644 (file)
@@ -32,7 +32,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
                graphCmd.AddArguments("--all")
        }
 
-       graphCmd.AddArguments("-C", "-M", "--date=iso").
+       graphCmd.AddArguments("-C", "-M", "--date=iso-strict").
                AddOptionFormat("-n %d", setting.UI.GraphMaxCommitNum*page).
                AddOptionFormat("--pretty=format:%s", format)
 
index e48fef8b9d0cc85a3d230d5a7a12ba07fbd0670c..191b0b3afce9ba5e05392b0688be5ca29aad9701 100644 (file)
@@ -8,6 +8,7 @@ import (
        "context"
        "fmt"
        "strings"
+       "time"
 
        asymkey_model "code.gitea.io/gitea/models/asymkey"
        "code.gitea.io/gitea/models/db"
@@ -192,6 +193,14 @@ var RelationCommit = &Commit{
        Row: -1,
 }
 
+func parseGitTime(timeStr string) time.Time {
+       t, err := time.Parse(time.RFC3339, timeStr)
+       if err != nil {
+               return time.Unix(0, 0)
+       }
+       return t
+}
+
 // NewCommit creates a new commit from a provided line
 func NewCommit(row, column int, line []byte) (*Commit, error) {
        data := bytes.SplitN(line, []byte("|"), 5)
@@ -206,7 +215,7 @@ func NewCommit(row, column int, line []byte) (*Commit, error) {
                // 1 matches git log --pretty=format:%H => commit hash
                Rev: string(data[1]),
                // 2 matches git log --pretty=format:%ad => author date (format respects --date= option)
-               Date: string(data[2]),
+               Date: parseGitTime(string(data[2])),
                // 3 matches git log --pretty=format:%h => abbreviated commit hash
                ShortRev: string(data[3]),
                // 4 matches git log --pretty=format:%s => subject
@@ -245,7 +254,7 @@ type Commit struct {
        Column       int
        Refs         []git.Reference
        Rev          string
-       Date         string
+       Date         time.Time
        ShortRev     string
        Subject      string
 }
index 4a794e6f30c7bc11be65c1082b52bec8e1badf5a..66f83d23fe5c203b3e211fe775d11c6677eb6214 100644 (file)
@@ -27,7 +27,7 @@ func (du *DateUtils) AbsoluteShort(time any) template.HTML {
 
 // AbsoluteLong renders in "January 01, 2006" format
 func (du *DateUtils) AbsoluteLong(time any) template.HTML {
-       return dateTimeFormat("short", time)
+       return dateTimeFormat("long", time)
 }
 
 // FullTime renders in "Jan 01, 2006 20:33:44" format
index e86fb3ccb1c52889c561d2c974bb1ea0121e380b..cbe709c030db4a19ea1418e275eabb13625f37c4 100644 (file)
@@ -26,6 +26,7 @@ import (
        "code.gitea.io/gitea/modules/timeutil"
        "code.gitea.io/gitea/modules/web"
        "code.gitea.io/gitea/routers/api/v1/utils"
+       "code.gitea.io/gitea/routers/common"
        "code.gitea.io/gitea/services/context"
        "code.gitea.io/gitea/services/convert"
        issue_service "code.gitea.io/gitea/services/issue"
@@ -1046,18 +1047,11 @@ func UpdateIssueDeadline(ctx *context.APIContext) {
                return
        }
 
-       var deadlineUnix timeutil.TimeStamp
-       var deadline time.Time
-       if form.Deadline != nil && !form.Deadline.IsZero() {
-               deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
-                       23, 59, 59, 0, time.Local)
-               deadlineUnix = timeutil.TimeStamp(deadline.Unix())
-       }
-
+       deadlineUnix, _ := common.ParseAPIDeadlineToEndOfDay(form.Deadline)
        if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
                ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err)
                return
        }
 
-       ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: &deadline})
+       ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: deadlineUnix.AsTimePtr()})
 }
index abe9e4006a8e4e1a0b24110e895f30ec0ddd44d2..78907c85a5847be351cb854d9fd1f418c6e99ae5 100644 (file)
@@ -7,7 +7,6 @@ package repo
 import (
        "net/http"
        "strconv"
-       "time"
 
        "code.gitea.io/gitea/models/db"
        issues_model "code.gitea.io/gitea/models/issues"
@@ -16,6 +15,7 @@ import (
        "code.gitea.io/gitea/modules/timeutil"
        "code.gitea.io/gitea/modules/web"
        "code.gitea.io/gitea/routers/api/v1/utils"
+       "code.gitea.io/gitea/routers/common"
        "code.gitea.io/gitea/services/context"
        "code.gitea.io/gitea/services/convert"
 )
@@ -155,16 +155,16 @@ func CreateMilestone(ctx *context.APIContext) {
        //     "$ref": "#/responses/notFound"
        form := web.GetForm(ctx).(*api.CreateMilestoneOption)
 
-       if form.Deadline == nil {
-               defaultDeadline, _ := time.ParseInLocation("2006-01-02", "9999-12-31", time.Local)
-               form.Deadline = &defaultDeadline
+       var deadlineUnix int64
+       if form.Deadline != nil {
+               deadlineUnix = form.Deadline.Unix()
        }
 
        milestone := &issues_model.Milestone{
                RepoID:       ctx.Repo.Repository.ID,
                Name:         form.Title,
                Content:      form.Description,
-               DeadlineUnix: timeutil.TimeStamp(form.Deadline.Unix()),
+               DeadlineUnix: timeutil.TimeStamp(deadlineUnix),
        }
 
        if form.State == "closed" {
@@ -225,9 +225,7 @@ func EditMilestone(ctx *context.APIContext) {
        if form.Description != nil {
                milestone.Content = *form.Description
        }
-       if form.Deadline != nil && !form.Deadline.IsZero() {
-               milestone.DeadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
-       }
+       milestone.DeadlineUnix, _ = common.ParseAPIDeadlineToEndOfDay(form.Deadline)
 
        oldIsClosed := milestone.IsClosed
        if form.State != nil {
diff --git a/routers/common/deadline.go b/routers/common/deadline.go
new file mode 100644 (file)
index 0000000..152e945
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package common
+
+import (
+       "time"
+
+       "code.gitea.io/gitea/modules/setting"
+       "code.gitea.io/gitea/modules/timeutil"
+)
+
+func ParseDeadlineDateToEndOfDay(date string) (timeutil.TimeStamp, error) {
+       if date == "" {
+               return 0, nil
+       }
+       deadline, err := time.ParseInLocation("2006-01-02", date, setting.DefaultUILocation)
+       if err != nil {
+               return 0, err
+       }
+       deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
+       return timeutil.TimeStamp(deadline.Unix()), nil
+}
+
+func ParseAPIDeadlineToEndOfDay(t *time.Time) (timeutil.TimeStamp, error) {
+       if t == nil || t.IsZero() || t.Unix() == 0 {
+               return 0, nil
+       }
+       deadline := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, setting.DefaultUILocation)
+       return timeutil.TimeStamp(deadline.Unix()), nil
+}
index 93e2b5e748662529e6595fbeb94c598592cd283e..1ee6e98afbd3d3152eaf7f412e6f8e92bf6d6d79 100644 (file)
@@ -17,7 +17,6 @@ import (
        "sort"
        "strconv"
        "strings"
-       "time"
 
        activities_model "code.gitea.io/gitea/models/activities"
        "code.gitea.io/gitea/models/db"
@@ -45,9 +44,9 @@ import (
        api "code.gitea.io/gitea/modules/structs"
        "code.gitea.io/gitea/modules/templates"
        "code.gitea.io/gitea/modules/templates/vars"
-       "code.gitea.io/gitea/modules/timeutil"
        "code.gitea.io/gitea/modules/util"
        "code.gitea.io/gitea/modules/web"
+       "code.gitea.io/gitea/routers/common"
        "code.gitea.io/gitea/routers/utils"
        shared_user "code.gitea.io/gitea/routers/web/shared/user"
        asymkey_service "code.gitea.io/gitea/services/asymkey"
@@ -2329,7 +2328,6 @@ func UpdateIssueContent(ctx *context.Context) {
 
 // UpdateIssueDeadline updates an issue deadline
 func UpdateIssueDeadline(ctx *context.Context) {
-       form := web.GetForm(ctx).(*api.EditDeadlineOption)
        issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":index"))
        if err != nil {
                if issues_model.IsErrIssueNotExist(err) {
@@ -2345,20 +2343,13 @@ func UpdateIssueDeadline(ctx *context.Context) {
                return
        }
 
-       var deadlineUnix timeutil.TimeStamp
-       var deadline time.Time
-       if form.Deadline != nil && !form.Deadline.IsZero() {
-               deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
-                       23, 59, 59, 0, time.Local)
-               deadlineUnix = timeutil.TimeStamp(deadline.Unix())
-       }
-
+       deadlineUnix, _ := common.ParseDeadlineDateToEndOfDay(ctx.FormString("deadline"))
        if err := issues_model.UpdateIssueDeadline(ctx, issue, deadlineUnix, ctx.Doer); err != nil {
                ctx.Error(http.StatusInternalServerError, "UpdateIssueDeadline", err.Error())
                return
        }
 
-       ctx.JSON(http.StatusCreated, api.IssueDeadline{Deadline: &deadline})
+       ctx.JSONRedirect("")
 }
 
 // UpdateIssueMilestone change issue's milestone
index e4ee025875ef6fb6f35f085206427a63867b7437..5c0972188cecf1c70faac595c98d6015d097f5c6 100644 (file)
@@ -7,7 +7,6 @@ import (
        "fmt"
        "net/http"
        "net/url"
-       "time"
 
        "code.gitea.io/gitea/models/db"
        issues_model "code.gitea.io/gitea/models/issues"
@@ -16,8 +15,8 @@ import (
        "code.gitea.io/gitea/modules/markup/markdown"
        "code.gitea.io/gitea/modules/optional"
        "code.gitea.io/gitea/modules/setting"
-       "code.gitea.io/gitea/modules/timeutil"
        "code.gitea.io/gitea/modules/web"
+       "code.gitea.io/gitea/routers/common"
        "code.gitea.io/gitea/services/context"
        "code.gitea.io/gitea/services/forms"
        "code.gitea.io/gitea/services/issue"
@@ -134,22 +133,18 @@ func NewMilestonePost(ctx *context.Context) {
                return
        }
 
-       if len(form.Deadline) == 0 {
-               form.Deadline = "9999-12-31"
-       }
-       deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
+       deadlineUnix, err := common.ParseDeadlineDateToEndOfDay(form.Deadline)
        if err != nil {
                ctx.Data["Err_Deadline"] = true
                ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
                return
        }
 
-       deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
-       if err = issues_model.NewMilestone(ctx, &issues_model.Milestone{
+       if err := issues_model.NewMilestone(ctx, &issues_model.Milestone{
                RepoID:       ctx.Repo.Repository.ID,
                Name:         form.Title,
                Content:      form.Content,
-               DeadlineUnix: timeutil.TimeStamp(deadline.Unix()),
+               DeadlineUnix: deadlineUnix,
        }); err != nil {
                ctx.ServerError("NewMilestone", err)
                return
@@ -194,17 +189,13 @@ func EditMilestonePost(ctx *context.Context) {
                return
        }
 
-       if len(form.Deadline) == 0 {
-               form.Deadline = "9999-12-31"
-       }
-       deadline, err := time.ParseInLocation("2006-01-02", form.Deadline, time.Local)
+       deadlineUnix, err := common.ParseDeadlineDateToEndOfDay(form.Deadline)
        if err != nil {
                ctx.Data["Err_Deadline"] = true
                ctx.RenderWithErr(ctx.Tr("repo.milestones.invalid_due_date_format"), tplMilestoneNew, &form)
                return
        }
 
-       deadline = time.Date(deadline.Year(), deadline.Month(), deadline.Day(), 23, 59, 59, 0, deadline.Location())
        m, err := issues_model.GetMilestoneByRepoID(ctx, ctx.Repo.Repository.ID, ctx.PathParamInt64(":id"))
        if err != nil {
                if issues_model.IsErrMilestoneNotExist(err) {
@@ -216,7 +207,7 @@ func EditMilestonePost(ctx *context.Context) {
        }
        m.Name = form.Title
        m.Content = form.Content
-       m.DeadlineUnix = timeutil.TimeStamp(deadline.Unix())
+       m.DeadlineUnix = deadlineUnix
        if err = issues_model.UpdateMilestone(ctx, m, m.IsClosed); err != nil {
                ctx.ServerError("UpdateMilestone", err)
                return
index 69ed4bd8e449d78081863433c2d5e29774a48709..29dd8a8edcb6689912dd90f0764be9594e9be68d 100644 (file)
@@ -1208,7 +1208,7 @@ func registerRoutes(m *web.Router) {
                        m.Group("/{index}", func() {
                                m.Post("/title", repo.UpdateIssueTitle)
                                m.Post("/content", repo.UpdateIssueContent)
-                               m.Post("/deadline", web.Bind(structs.EditDeadlineOption{}), repo.UpdateIssueDeadline)
+                               m.Post("/deadline", repo.UpdateIssueDeadline)
                                m.Post("/watch", repo.IssueWatch)
                                m.Post("/ref", repo.UpdateIssueRef)
                                m.Post("/pin", reqRepoAdmin, repo.IssuePinOrUnpin)
index f514dc431351e516ef00b20d6c283dbb40d07151..e3124efd6402e4dd695aae4e604f832f559c7e5f 100644 (file)
@@ -260,7 +260,7 @@ func ToAPIMilestone(m *issues_model.Milestone) *api.Milestone {
        if m.IsClosed {
                apiMilestone.Closed = m.ClosedDateUnix.AsTimePtr()
        }
-       if m.DeadlineUnix.Year() < 9999 {
+       if m.DeadlineUnix > 0 {
                apiMilestone.Deadline = m.DeadlineUnix.AsTimePtr()
        }
        return apiMilestone
index 17c6cad86b89a5d016c7ce132c28d4073ee089ee..f1d0e62330274414190f9d9dd8edad2c813b345a 100644 (file)
@@ -56,7 +56,7 @@
                                                        {{end}}
                                                {{end}}
                                        </span>
-                                       <span class="author tw-flex tw-items-center tw-mr-2 tw-gap-[1px]">
+                                       <span class="author tw-flex tw-items-center tw-mr-2 tw-gap-1">
                                                {{$userName := $commit.Commit.Author.Name}}
                                                {{if $commit.User}}
                                                        {{if and $commit.User.FullName DefaultShowFullName}}
index 9f32df00e39f03adf63f9f7f463431a1a2f787e5..736a75d73a1b3e559ebc27225e92633df757df43 100644 (file)
@@ -30,9 +30,9 @@
                                <div class="field {{if .Err_Deadline}}error{{end}}">
                                        <label>
                                                {{ctx.Locale.Tr "repo.milestones.due_date"}}
-                                               <a id="clear-date">{{ctx.Locale.Tr "repo.milestones.clear"}}</a>
+                                               <a id="milestone-clear-deadline">{{ctx.Locale.Tr "repo.milestones.clear"}}</a>
                                        </label>
-                                       <input type="date" id="deadline" name="deadline" value="{{.deadline}}" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}">
+                                       <input type="date" name="deadline" class="tw-w-auto" value="{{.deadline}}" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}">
                                </div>
                                <div class="field">
                                        <label>{{ctx.Locale.Tr "repo.milestones.desc"}}</label>
index 01fa43397819988852c0ccb94af51f740e4a75f7..9c1acae0cfb0e686a348fb621d464c7d98780f33 100644 (file)
 
        <div class="divider"></div>
        <span class="text"><strong>{{ctx.Locale.Tr "repo.issues.due_date"}}</strong></span>
-       <div class="ui form" id="deadline-loader">
-               <div class="ui negative message tw-hidden" id="deadline-err-invalid-date">
-                       {{svg "octicon-x" 16 "close icon"}}
-                       {{ctx.Locale.Tr "repo.issues.due_date_invalid"}}
-               </div>
-               {{if ne .Issue.DeadlineUnix 0}}
-                       <p>
-                               <div class="tw-flex tw-justify-between tw-items-center">
-                                       <div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
-                                               {{svg "octicon-calendar" 16 "tw-mr-2"}}
-                                               {{DateUtils.AbsoluteLong .Issue.DeadlineUnix}}
-                                       </div>
-                                       <div>
-                                               {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
-                                                       <a class="issue-due-edit muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_edit"}}">{{svg "octicon-pencil" 16 "tw-mr-1"}}</a>
-                                                       <a class="issue-due-remove muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_remove"}}">{{svg "octicon-trash"}}</a>
-                                               {{end}}
-                                       </div>
+       <div class="ui form tw-mt-2">
+               {{if .Issue.DeadlineUnix}}
+                       <div class="tw-flex tw-justify-between tw-items-center tw-gap-2">
+                               <div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
+                                       {{svg "octicon-calendar"}} {{DateUtils.AbsoluteLong .Issue.DeadlineUnix}}
+                               </div>
+                               <div class="flex-text-block">
+                                       {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
+                                               <a class="issue-due-edit muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_edit"}}">{{svg "octicon-pencil"}}</a>
+                                               <a class="issue-due-remove muted" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_form_remove"}}">{{svg "octicon-trash"}}</a>
+                                       {{end}}
                                </div>
-                       </p>
+                       </div>
                {{else}}
-                       <p>{{ctx.Locale.Tr "repo.issues.due_date_not_set"}}</p>
+                       {{ctx.Locale.Tr "repo.issues.due_date_not_set"}}
                {{end}}
 
                {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
-                       <div {{if ne .Issue.DeadlineUnix 0}} class="tw-hidden"{{end}} id="deadlineForm">
-                               <form class="ui fluid action input issue-due-form" action="{{AppSubUrl}}/{{PathEscape .Repository.Owner.Name}}/{{PathEscape .Repository.Name}}/issues/{{.Issue.Index}}/deadline" method="post" id="update-issue-deadline-form">
-                                       {{$.CsrfTokenHtml}}
-                                       <input required placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.FormatDate}}"{{end}} type="date" name="deadlineDate" id="deadlineDate">
-                                       <button class="ui icon button">
-                                               {{if ne .Issue.DeadlineUnix 0}}
-                                                       {{svg "octicon-pencil"}}
-                                               {{else}}
-                                                       {{svg "octicon-plus"}}
-                                               {{end}}
-                                       </button>
-                               </form>
-                       </div>
+                       <form class="ui fluid action input issue-due-form form-fetch-action tw-mt-2 {{if .Issue.DeadlineUnix}}tw-hidden{{end}}"
+                                               method="post" action="{{AppSubUrl}}/{{PathEscape .Repository.Owner.Name}}/{{PathEscape .Repository.Name}}/issues/{{.Issue.Index}}/deadline"
+                       >
+                               {{$.CsrfTokenHtml}}
+                               <input required type="date" name="deadline" placeholder="{{ctx.Locale.Tr "repo.issues.due_date_form"}}" {{if .Issue.DeadlineUnix}}value="{{.Issue.DeadlineUnix.FormatDate}}"{{end}}>
+                               <button class="ui icon button">{{Iif .Issue.DeadlineUnix (svg "octicon-pencil") (svg "octicon-plus")}}</button>
+                       </form>
                {{end}}
        </div>
 
index 32ac56298fab4281e3807055ce7ff998a7b69470..2d00752302ff27fd752303f3d4b95e7f8265e29b 100644 (file)
@@ -59,6 +59,7 @@ func TestAPIIssuesMilestone(t *testing.T) {
        DecodeJSON(t, resp, &apiMilestone)
        assert.Equal(t, "wow", apiMilestone.Title)
        assert.Equal(t, structs.StateClosed, apiMilestone.State)
+       assert.Nil(t, apiMilestone.Deadline)
 
        var apiMilestones []structs.Milestone
        req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones?state=%s", owner.Name, repo.Name, "all")).
@@ -66,6 +67,7 @@ func TestAPIIssuesMilestone(t *testing.T) {
        resp = MakeRequest(t, req, http.StatusOK)
        DecodeJSON(t, resp, &apiMilestones)
        assert.Len(t, apiMilestones, 4)
+       assert.Nil(t, apiMilestones[0].Deadline)
 
        req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/milestones/%s", owner.Name, repo.Name, apiMilestones[2].Title)).
                AddTokenAuth(token)
index df45da84a55deb8c6b387479261fe832e1578241..4617c5f89a14932525ca4f690bfa6ad906515d09 100644 (file)
@@ -657,26 +657,21 @@ func TestUpdateIssueDeadline(t *testing.T) {
        repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID})
        owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID})
        assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext))
-       assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
+       assert.Equal(t, "2002-04-20", issueBefore.DeadlineUnix.FormatDate())
        assert.Equal(t, api.StateOpen, issueBefore.State())
 
        session := loginUser(t, owner.Name)
+       urlStr := fmt.Sprintf("%s/%s/issues/%d/deadline?_csrf=%s", owner.Name, repoBefore.Name, issueBefore.Index, GetUserCSRFToken(t, session))
 
-       issueURL := fmt.Sprintf("%s/%s/issues/%d", owner.Name, repoBefore.Name, issueBefore.Index)
-       req := NewRequest(t, "GET", issueURL)
-       resp := session.MakeRequest(t, req, http.StatusOK)
-       htmlDoc := NewHTMLParser(t, resp.Body)
-
-       urlStr := issueURL + "/deadline?_csrf=" + htmlDoc.GetCSRF()
-       req = NewRequestWithJSON(t, "POST", urlStr, map[string]string{
-               "due_date": "2022-04-06T00:00:00.000Z",
-       })
-
-       resp = session.MakeRequest(t, req, http.StatusCreated)
-       var apiIssue api.IssueDeadline
-       DecodeJSON(t, resp, &apiIssue)
+       req := NewRequestWithValues(t, "POST", urlStr, map[string]string{"deadline": "2022-04-06"})
+       session.MakeRequest(t, req, http.StatusOK)
+       issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
+       assert.EqualValues(t, "2022-04-06", issueAfter.DeadlineUnix.FormatDate())
 
-       assert.EqualValues(t, "2022-04-06", apiIssue.Deadline.Format("2006-01-02"))
+       req = NewRequestWithValues(t, "POST", urlStr, map[string]string{"deadline": ""})
+       session.MakeRequest(t, req, http.StatusOK)
+       issueAfter = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10})
+       assert.True(t, issueAfter.DeadlineUnix.IsZero())
 }
 
 func TestIssueReferenceURL(t *testing.T) {
index f33e192f291a4ae6bcc4c14baf13f85729dcb87d..0d30d8103c19ffe67a0fe183b8d716f0074371c3 100644 (file)
@@ -3,6 +3,7 @@ import {POST} from '../modules/fetch.ts';
 import {updateIssuesMeta} from './repo-common.ts';
 import {svg} from '../svg.ts';
 import {htmlEscape} from 'escape-goat';
+import {toggleElem} from '../utils/dom.ts';
 
 // if there are draft comments, confirm before reloading, to avoid losing comments
 function reloadConfirmDraftComment() {
@@ -258,8 +259,22 @@ function selectItem(select_id, input_id) {
   });
 }
 
+function initRepoIssueDue() {
+  const form = document.querySelector<HTMLFormElement>('.issue-due-form');
+  if (!form) return;
+  const deadline = form.querySelector<HTMLInputElement>('input[name=deadline]');
+  document.querySelector('.issue-due-edit')?.addEventListener('click', () => {
+    toggleElem(form);
+  });
+  document.querySelector('.issue-due-remove')?.addEventListener('click', () => {
+    deadline.value = '';
+    form.dispatchEvent(new Event('submit', {cancelable: true, bubbles: true}));
+  });
+}
+
 export function initRepoIssueSidebar() {
   initBranchSelector();
+  initRepoIssueDue();
 
   // Init labels and assignees
   initListSubmits('select-label', 'labels');
index a9a65cdc819bc7da85a49fd690ba3d0321be007d..392af776f8804e103903dc055d63eecb73740254 100644 (file)
@@ -43,52 +43,6 @@ export function initRepoIssueTimeTracking() {
   });
 }
 
-async function updateDeadline(deadlineString) {
-  hideElem('#deadline-err-invalid-date');
-  document.querySelector('#deadline-loader')?.classList.add('is-loading');
-
-  let realDeadline = null;
-  if (deadlineString !== '') {
-    const newDate = Date.parse(deadlineString);
-
-    if (Number.isNaN(newDate)) {
-      document.querySelector('#deadline-loader')?.classList.remove('is-loading');
-      showElem('#deadline-err-invalid-date');
-      return false;
-    }
-    realDeadline = new Date(newDate);
-  }
-
-  try {
-    const response = await POST(document.querySelector('#update-issue-deadline-form').getAttribute('action'), {
-      data: {due_date: realDeadline},
-    });
-
-    if (response.ok) {
-      window.location.reload();
-    } else {
-      throw new Error('Invalid response');
-    }
-  } catch (error) {
-    console.error(error);
-    document.querySelector('#deadline-loader').classList.remove('is-loading');
-    showElem('#deadline-err-invalid-date');
-  }
-}
-
-export function initRepoIssueDue() {
-  $(document).on('click', '.issue-due-edit', () => {
-    toggleElem('#deadlineForm');
-  });
-  $(document).on('click', '.issue-due-remove', () => {
-    updateDeadline('');
-  });
-  $(document).on('submit', '.issue-due-form', () => {
-    updateDeadline($('#deadlineDate').val());
-    return false;
-  });
-}
-
 /**
  * @param {HTMLElement} item
  */
index ddef723b48c857cd1a0167e61855d68a06caa386..ee704ea2e06a46e8fc92247ddaf0fc5085fca3d7 100644 (file)
@@ -1,11 +1,9 @@
-import $ from 'jquery';
-
 export function initRepoMilestone() {
-  // Milestones
-  if ($('.repository.new.milestone').length > 0) {
-    $('#clear-date').on('click', () => {
-      $('#deadline').val('');
-      return false;
-    });
-  }
+  const page = document.querySelector('.repository.new.milestone');
+  if (!page) return;
+
+  const deadline = page.querySelector<HTMLInputElement>('form input[name=deadline]');
+  document.querySelector('#milestone-clear-deadline').addEventListener('click', () => {
+    deadline.value = '';
+  });
 }
index 08d8997fd181618589418f235223111e8b61dbc3..487aac97aa3a59e8c9e71626ddde10779b5d0c94 100644 (file)
@@ -25,7 +25,6 @@ import {initPdfViewer} from './render/pdf.ts';
 
 import {initUserAuthOauth2, initUserCheckAppUrl} from './features/user-auth.ts';
 import {
-  initRepoIssueDue,
   initRepoIssueReferenceRepositorySearch,
   initRepoIssueTimeTracking,
   initRepoIssueWipTitle,
@@ -181,7 +180,6 @@ onDomReady(() => {
     initRepoEditor,
     initRepoGraphGit,
     initRepoIssueContentHistory,
-    initRepoIssueDue,
     initRepoIssueList,
     initRepoIssueSidebarList,
     initArchivedLabelHandler,