Issue due date api (#3890)

* Implemented basic api endpoint to manage deadlines

* Fixed checking for permissions

* Updating a deadline from the ui is now entirely done via the api

* cleanup

* Cosmetics

* fixed lint + fmt

* Added swagger model definition for deadline response

* Updated gitea-sdk

* Updated gitea-sdk

* More cleanup

* Generate swagger json

* Merge branch 'master' of https://github.com/go-gitea/gitea into issue-due-date-api

# Conflicts:
#	public/swagger.v1.json

* Fixed permission to update a deadline via api

* Re-added form to change a deadline

* Added client-side validation + not ignore error messages from the api

* Added locale for error message

* Merge branch 'master' of https://github.com/go-gitea/gitea

# Conflicts:
#	models/issue_comment.go

* Proper date validation

* Fixed indention

* moved css to css file

* added documentation for error codes

* after merge cleanup

* Added swagger description

* DO NOTHING BUT TRIGGER THAT F*CKIN CI SO IT PICKS UP THE LATEST COMMIT AS IT SHOULD

* DO NOTHING BUT TRIGGER THAT F*CKIN CI SO IT PICKS UP THE LATEST COMMIT AS IT SHOULD

* regenerated stylesheets
This commit is contained in:
kolaente authored and Jonas Franz committed 2018-07-16 14:43:00 +02:00
1 parent 55d9ddf24a
commit ef6813abc9
12 files changed
+258 -90

No files matched your search

+1
View File
@@ -781,6 +781,7 @@ issues.due_date_added = "added the due date %s %s"
issues.due_date_modified = "modified the due date to %s from %s %s"
issues.due_date_remove = "removed the due date %s %s"
issues.due_date_overdue = "Overdue"
issues.due_date_invalid = "The due date is invalid or out of range. Please use the format yyyy-mm-dd."
pulls.desc = Enable merge requests and code reviews.
pulls.new = New Pull Request
+1 -1
View File
@@ -1 +1 @@
.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}body{font-family:Lato,"Segoe UI","Microsoft YaHei",Arial,Helvetica,sans-serif!important;background-color:#fff;overflow-y:scroll;-webkit-font-smoothing:antialiased}img{border-radius:3px}.rounded{border-radius:.28571429rem!important}code,pre{font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;-ms-word-break:break-all;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.full.height{padding:0;margin:0 0 -40px 0;min-height:100%}.following.bar{z-index:900;left:0;width:100%;margin:0}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;display:-ms-flexbox;-ms-flex-align:inherit;align-items:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-moz-user-select:auto;-ms-user-select:auto;-webkit-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.floating.label{z-index:10}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui.dropdown .menu>.item>.floating.label{z-index:11}.ui.dropdown .menu .menu>.item>.floating.label{z-index:21}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:Consolas,Menlo,Monaco,"Lucida Console",monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .backgroLine truncated
.tribute-container{box-shadow:0 1px 3px 1px #c7c7c7}.tribute-container ul{background:#fff}.tribute-container li{padding:8px 12px;border-bottom:1px solid #dcdcdc}.tribute-container li img{display:inline-block;vertical-align:middle;width:28px;height:28px;margin-right:5px}.tribute-container li span.fullname{font-weight:400;font-size:.8rem;margin-left:3px}.tribute-container li.highlight,.tribute-container li:hover{background:#2185D0;color:#fff}.emoji{width:1.5em;height:1.5em;display:inline-block;background-size:contain}body{font-family:Lato,"Segoe UI","Microsoft YaHei",Arial,Helvetica,sans-serif!important;background-color:#fff;overflow-y:scroll;-webkit-font-smoothing:antialiased}img{border-radius:3px}.rounded{border-radius:.28571429rem!important}code,pre{font:12px Consolas,"Liberation Mono",Menlo,Courier,monospace}code.raw,pre.raw{padding:7px 12px;margin:10px 0;background-color:#f8f8f8;border:1px solid #ddd;border-radius:3px;font-size:13px;line-height:1.5;overflow:auto}code.wrap,pre.wrap{white-space:pre-wrap;-ms-word-break:break-all;word-break:break-all;overflow-wrap:break-word;word-wrap:break-word}.dont-break-out{overflow-wrap:break-word;word-wrap:break-word;-ms-word-break:break-all;word-break:break-all;-ms-hyphens:auto;-moz-hyphens:auto;-webkit-hyphens:auto;hyphens:auto}.full.height{padding:0;margin:0 0 -40px 0;min-height:100%}.following.bar{z-index:900;left:0;width:100%;margin:0}.following.bar.light{background-color:#fff;border-bottom:1px solid #DDD;box-shadow:0 2px 3px rgba(0,0,0,.04)}.following.bar .column .menu{margin-top:0}.following.bar .top.menu a.item.brand{padding-left:0}.following.bar .brand .ui.mini.image{width:30px}.following.bar .top.menu .dropdown.item.active,.following.bar .top.menu .dropdown.item:hover,.following.bar .top.menu a.item:hover{background-color:transparent}.following.bar .top.menu a.item:hover{color:rgba(0,0,0,.45)}.following.bar .top.menu .menu{z-index:900}.following.bar .octicon{margin-right:.75em}.following.bar .octicon.fitted{margin-right:0}.following.bar .searchbox{background-color:#f4f4f4!important}.following.bar .searchbox:focus{background-color:#e9e9e9!important}.following.bar .text .octicon{width:16px;text-align:center}@media only screen and (max-width:767px){.following.bar #navbar:not(.shown)>:not(:first-child){display:none}}.right.stackable.menu{margin-left:auto;display:flex;display:-ms-flexbox;-ms-flex-align:inherit;align-items:inherit;-ms-flex-direction:inherit;flex-direction:inherit}.ui.left{float:left}.ui.right{float:right}.ui.button,.ui.menu .item{-moz-user-select:auto;-ms-user-select:auto;-webkit-user-select:auto;user-select:auto}.ui.container.fluid.padded{padding:0 10px 0 10px}.ui.form .ui.button{font-weight:400}.ui.floating.label{z-index:10}.ui.menu,.ui.segment,.ui.vertical.menu{box-shadow:none}.ui .menu:not(.vertical) .item>.button.compact{padding:.58928571em 1.125em}.ui .menu:not(.vertical) .item>.button.small{font-size:.92857143rem}.ui.dropdown .menu>.item>.floating.label{z-index:11}.ui.dropdown .menu .menu>.item>.floating.label{z-index:21}.ui .text.red{color:#d95c5c!important}.ui .text.red a{color:#d95c5c!important}.ui .text.red a:hover{color:#E67777!important}.ui .text.blue{color:#428bca!important}.ui .text.blue a{color:#15c!important}.ui .text.blue a:hover{color:#428bca!important}.ui .text.black{color:#444}.ui .text.black:hover{color:#000}.ui .text.grey{color:#767676!important}.ui .text.grey a{color:#444!important}.ui .text.grey a:hover{color:#000!important}.ui .text.light.grey{color:#888!important}.ui .text.green{color:#6cc644!important}.ui .text.purple{color:#6e5494!important}.ui .text.yellow{color:#FBBD08!important}.ui .text.gold{color:#a1882b!important}.ui .text.left{text-align:left!important}.ui .text.right{text-align:right!important}.ui .text.small{font-size:.75em}.ui .text.normal{font-weight:400}.ui .text.bold{font-weight:700}.ui .text.italic{font-style:italic}.ui .text.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.ui .text.thin{font-weight:400}.ui .text.middle{vertical-align:middle}.ui .message{text-align:center}.ui .header>i+.content{padding-left:.75rem;vertical-align:middle}.ui .warning.header{background-color:#F9EDBE!important;border-color:#F0C36D}.ui .warning.segment{border-color:#F0C36D}.ui .info.segment{border:1px solid #c5d5dd}.ui .info.segment.top{background-color:#e6f1f6!important}.ui .info.segment.top h3,.ui .info.segment.top h4{margin-top:0}.ui .info.segment.top h3:last-child{margin-top:4px}.ui .info.segment.top>:last-child{margin-bottom:0}.ui .normal.header{font-weight:400}.ui .avatar.image{border-radius:3px}.ui .form .fake{display:none!important}.ui .form .sub.field{margin-left:25px}.ui .sha.label{font-family:Consolas,Menlo,Monaco,"Lucida Console",monospace;font-size:13px;padding:6px 10px 4px 10px;font-weight:400;margin:0 6px}.ui.status.buttons .octicon{margin-right:4px}.ui.inline.delete-button{padding:8px 15px;font-weight:400}.ui .background.red{background-color:#d95c5c!important}.ui .backgroLine truncated
+37 -7
View File
@@ -2447,14 +2447,44 @@ function initTopicbar() {
}
});
}
function toggleDuedateForm() {
$('#add_deadline_form').fadeToggle(150);
function toggleDeadlineForm() {
$('#deadlineForm').fadeToggle(150);
}
function deleteDueDate(url) {
$.post(url, {
'_csrf': csrf,
},function( data ) {
window.location.reload();
function setDeadline() {
var deadline = $('#deadlineDate').val();
updateDeadline(deadline);
}
function updateDeadline(deadlineString) {
$('#deadline-err-invalid-date').hide();
$('#deadline-loader').addClass('loading');
var realDeadline = null;
if (deadlineString !== '') {
var newDate = Date.parse(deadlineString)
if (isNaN(newDate)) {
$('#deadline-loader').removeClass('loading');
$('#deadline-err-invalid-date').show();
return false;
}
realDeadline = new Date(newDate);
}
$.ajax($('#update-issue-deadline-form').attr('action') + '/deadline', {
data: JSON.stringify({
'due_date': realDeadline,
}),
contentType: 'application/json',
type: 'POST',
success: function () {
window.location.reload();
},
error: function () {
$('#deadline-loader').removeClass('loading');
$('#deadline-err-invalid-date').show();
}
});
}
+7
View File
@@ -98,6 +98,13 @@
}
}
}
#deadlineForm input{
width: 12.8rem;
border-radius: 4px 0 0 4px;
border-right: 0;
white-space: nowrap;
}
}
.header-wrapper {
background-color: #FAFAFA;
+95
View File
@@ -2320,6 +2320,68 @@
}
}
},
"/repos/{owner}/{repo}/issues/{index}/deadline": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"issue"
],
"summary": "Set an issue deadline. If set to null, the deadline is deleted.",
"operationId": "issueEditIssueDeadline",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "index of the issue to create or update a deadline on",
"name": "index",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/EditDeadlineOption"
}
}
],
"responses": {
"201": {
"$ref": "#/responses/IssueDeadline"
},
"403": {
"description": "Not repo writer",
"schema": {
"$ref": "#/responses/forbidden"
}
},
"404": {
"description": "Issue not found",
"schema": {
"$ref": "#/responses/empty"
}
}
}
}
},
"/repos/{owner}/{repo}/issues/{index}/labels": {
"get": {
"produces": [
@@ -6145,6 +6207,21 @@
},
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
},
"EditDeadlineOption": {
"description": "EditDeadlineOption options for creating a deadline",
"type": "object",
"required": [
"due_date"
],
"properties": {
"due_date": {
"type": "string",
"format": "date-time",
"x-go-name": "Deadline"
}
},
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
},
"EditHookOption": {
"description": "EditHookOption options when modify one hook",
"type": "object",
@@ -6635,6 +6712,18 @@
},
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
},
"IssueDeadline": {
"description": "IssueDeadline represents an issue deadline",
"type": "object",
"properties": {
"due_date": {
"type": "string",
"format": "date-time",
"x-go-name": "Deadline"
}
},
"x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea"
},
"IssueLabelsOption": {
"description": "IssueLabelsOption a collection of labels",
"type": "object",
@@ -7635,6 +7724,12 @@
"$ref": "#/definitions/Issue"
}
},
"IssueDeadline": {
"description": "IssueDeadline",
"schema": {
"$ref": "#/definitions/IssueDeadline"
}
},
"IssueList": {
"description": "IssueList",
"schema": {
+2
View File
@@ -447,6 +447,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("").Get(repo.ListTrackedTimes).
Post(reqToken(), bind(api.AddTimeOption{}), repo.AddTime)
})
m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline)
})
}, mustEnableIssues)
m.Group("/labels", func() {
+70 -1
View File
@@ -278,7 +278,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
// Update the deadline
var deadlineUnix util.TimeStamp
if form.Deadline != nil && !form.Deadline.IsZero() {
if form.Deadline != nil && !form.Deadline.IsZero() && ctx.Repo.IsWriter() {
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
}
@@ -338,3 +338,72 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
}
ctx.JSON(201, issue.APIFormat())
}
// UpdateIssueDeadline updates an issue deadline
func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/deadline issue issueEditIssueDeadline
// ---
// summary: Set an issue deadline. If set to null, the deadline is deleted.
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: index
// in: path
// description: index of the issue to create or update a deadline on
// type: integer
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditDeadlineOption"
// responses:
// "201":
// "$ref": "#/responses/IssueDeadline"
// "403":
// description: Not repo writer
// schema:
// "$ref": "#/responses/forbidden"
// "404":
// description: Issue not found
// schema:
// "$ref": "#/responses/empty"
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if models.IsErrIssueNotExist(err) {
ctx.Status(404)
} else {
ctx.Error(500, "GetIssueByIndex", err)
}
return
}
if !ctx.Repo.IsWriter() {
ctx.Status(403)
return
}
var deadlineUnix util.TimeStamp
if form.Deadline != nil && !form.Deadline.IsZero() {
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
}
if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
ctx.Error(500, "UpdateIssueDeadline", err)
return
}
ctx.JSON(201, api.IssueDeadline{Deadline: form.Deadline})
}
+7
View File
@@ -77,3 +77,10 @@ type swaggerResponseTrackedTimeList struct {
// in:body
Body []api.TrackedTime `json:"body"`
}
// IssueDeadline
// swagger:response IssueDeadline
type swaggerIssueDeadline struct {
// in:body
Body api.IssueDeadline `json:"body"`
}
+2
View File
@@ -32,6 +32,8 @@ type swaggerParameterBodies struct {
CreateIssueOption api.CreateIssueOption
// in:body
EditIssueOption api.EditIssueOption
// in:body
EditDeadlineOption api.EditDeadlineOption
// in:body
CreateIssueCommentOption api.CreateIssueCommentOption
-48
View File
@@ -1490,51 +1490,3 @@ func ChangeCommentReaction(ctx *context.Context, form auth.ReactionForm) {
"html": html,
})
}
// UpdateDeadline adds or updates a deadline
func UpdateDeadline(ctx *context.Context, form auth.DeadlineForm) {
issue := GetActionIssue(ctx)
if ctx.Written() {
return
}
if ctx.HasError() {
ctx.ServerError("ChangeIssueDeadline", errors.New(ctx.GetErrMsg()))
return
}
// Make unix of deadline string
deadline, err := time.ParseInLocation("2006-01-02", form.DateString, time.Local)
if err != nil {
ctx.Flash.Error(ctx.Tr("repo.issues.invalid_due_date_format"))
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index))
return
}
if err = models.UpdateIssueDeadline(issue, util.TimeStamp(deadline.Unix()), ctx.User); err != nil {
ctx.Flash.Error(ctx.Tr("repo.issues.error_modifying_due_date"))
}
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index))
return
}
// RemoveDeadline removes a deadline
func RemoveDeadline(ctx *context.Context) {
issue := GetActionIssue(ctx)
if ctx.Written() {
return
}
if ctx.HasError() {
ctx.ServerError("RemoveIssueDeadline", errors.New(ctx.GetErrMsg()))
return
}
if err := models.UpdateIssueDeadline(issue, 0, ctx.User); err != nil {
ctx.Flash.Error(ctx.Tr("repo.issues.error_removing_due_date"))
}
ctx.Redirect(fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, issue.Index))
return
}
-2
View File
@@ -532,8 +532,6 @@ func RegisterRoutes(m *macaron.Macaron) {
})
})
m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction)
m.Post("/deadline/update", reqRepoWriter, bindIgnErr(auth.DeadlineForm{}), repo.UpdateDeadline)
m.Post("/deadline/delete", reqRepoWriter, repo.RemoveDeadline)
})
m.Post("/labels", reqRepoWriter, repo.UpdateIssueLabel)
+36 -31
View File
@@ -211,38 +211,43 @@
<div class="ui divider"></div>
<span class="text"><strong>{{.i18n.Tr "repo.issues.due_date"}}</strong></span>
{{if ne .Issue.DeadlineUnix 0}}
<p>
<span class="octicon octicon-calendar"></span>
{{.Issue.DeadlineUnix.FormatShort}}
{{if .Issue.IsOverdue}}
<span style="color: red;">{{.i18n.Tr "repo.issues.due_date_overdue"}}</span>
{{end}}
{{if and .IsSigned .IsRepositoryWriter}}
<br/>
<a style="cursor:pointer;" onclick="toggleDuedateForm();"><i class="edit icon"></i>Edit</a> -
<a style="cursor:pointer;" onclick="deleteDueDate('{{$.RepoLink}}/issues/{{.Issue.Index}}/deadline/delete');"><i class="remove icon"></i>Remove</a>
{{end}}
</p>
{{else}}
<p><i>{{.i18n.Tr "repo.issues.due_date_not_set"}}</i></p>
{{end}}
{{if and .IsSigned .IsRepositoryWriter}}
<form method="POST" {{if ne .Issue.DeadlineUnix 0}}style="display: none;"{{end}} id="add_deadline_form" action="{{$.RepoLink}}/issues/{{.Issue.Index}}/deadline/update" class="ui action input fluid">
{{$.CsrfTokenHtml}}
<div class="ui fluid action input">
<input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if ne .Issue.DeadlineUnix 0 }}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="date" style="min-width: 13.9rem;border-radius: 4px 0 0 4px;border-right: 0;white-space: nowrap;">
<button class="ui green icon button">
{{if ne .Issue.DeadlineUnix 0}}
<i class="edit icon"></i>
{{else}}
<i class="plus icon"></i>
<div class="ui form" id="deadline-loader">
<div class="ui negative message" id="deadline-err-invalid-date" style="display: none;">
<i class="close icon"></i>
{{.i18n.Tr "repo.issues.due_date_invalid"}}
</div>
{{if ne .Issue.DeadlineUnix 0}}
<p>
<span class="octicon octicon-calendar"></span>
{{.Issue.DeadlineUnix.FormatShort}}
{{if .Issue.IsOverdue}}
<span style="color: red;">{{.i18n.Tr "repo.issues.due_date_overdue"}}</span>
{{end}}
</button>
</div>
</form>
{{end}}
{{if and .IsSigned .IsRepositoryWriter}}
<br/>
<a style="cursor:pointer;" onclick="toggleDeadlineForm();"><i class="edit icon"></i>Edit</a> -
<a style="cursor:pointer;" onclick="updateDeadline('');"><i class="remove icon"></i>Remove</a>
{{end}}
</p>
{{else}}
<p><i>{{.i18n.Tr "repo.issues.due_date_not_set"}}</i></p>
{{end}}
{{if and .IsSigned .IsRepositoryWriter}}
<div {{if ne .Issue.DeadlineUnix 0}} style="display: none;"{{end}} id="deadlineForm">
<form class="ui fluid action input" action="{{AppSubUrl}}/api/v1/repos/{{.Repository.Owner.Name}}/{{.Repository.Name}}/issues/{{.Issue.Index}}" method="post" id="update-issue-deadline-form" onsubmit="setDeadline();return false;">
{{$.CsrfTokenHtml}}
<input required placeholder="{{.i18n.Tr "repo.issues.due_date_form"}}" {{if gt .Issue.DeadlineUnix 0}}value="{{.Issue.DeadlineUnix.Format "2006-01-02"}}"{{end}} type="date" name="deadlineDate" id="deadlineDate">
<button class="ui green icon button">
{{if ne .Issue.DeadlineUnix 0}}
<i class="edit icon"></i>
{{else}}
<i class="plus icon"></i>
{{end}}
</button>
</form>
</div>
{{end}}
</div>
</div>
</div>