
IssueSearch: group milestones by state using optgroup
@1c134a3337a5f3b3ca9c7ed1c9eb52619ba1da84
--- app/assets/stylesheets/less/_override.less
+++ app/assets/stylesheets/less/_override.less
... | ... | @@ -91,9 +91,9 @@ |
91 | 91 |
|
92 | 92 |
.select2-results { |
93 | 93 |
max-height: 400px; |
94 |
- padding:0; |
|
94 |
+ padding:4px; margin:4px 0 0 0; |
|
95 | 95 |
|
96 |
- li { margin-bottom:1px; padding-left:4px; } |
|
96 |
+ li { margin-bottom:1px; } |
|
97 | 97 |
|
98 | 98 |
.avatar-wrap { margin-top:0; } |
99 | 99 |
|
... | ... | @@ -115,6 +115,11 @@ |
115 | 115 |
padding:3px 7px 4px 8px; |
116 | 116 |
background:#fafafa; color:#aaa; |
117 | 117 |
} |
118 |
+ |
|
119 |
+ .select2-result-with-children { |
|
120 |
+ border-top:1px solid #eee; |
|
121 |
+ padding-top:4px; margin-top:4px; |
|
122 |
+ } |
|
118 | 123 |
} |
119 | 124 |
|
120 | 125 |
|
--- app/views/common/select2.scala.html
+++ app/views/common/select2.scala.html
... | ... | @@ -41,8 +41,7 @@ |
41 | 41 |
</div> |
42 | 42 |
</script> |
43 | 43 |
<script id="tplSelect2FormatMilestone" type="text/x-jquery-tmpl"> |
44 |
-<div title="${name}"> |
|
45 |
- <span class="label milestone-state ${state}">${stateLabel}</span> |
|
44 |
+<div title="[${stateLabel}] ${name}"> |
|
46 | 45 |
${name} |
47 | 46 |
</div> |
48 | 47 |
</script> |
--- app/views/issue/create.scala.html
+++ app/views/issue/create.scala.html
... | ... | @@ -92,7 +92,7 @@ |
92 | 92 |
<dl id="milestoneOption" class="issue-option"> |
93 | 93 |
<dt>@Messages("milestone")</dt> |
94 | 94 |
<dd> |
95 |
- @defining(Milestone.findByProjectId(project.id)) { milestones => |
|
95 |
+ @defining(Milestone.findOpenMilestones(project.id)) { milestones => |
|
96 | 96 |
@if(milestones.isEmpty()) { |
97 | 97 |
<a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" target="_blank" class="ybtn ybtn-success ybtn-small">@Messages("milestone.menu.new")</a> |
98 | 98 |
} else { |
--- app/views/issue/edit.scala.html
+++ app/views/issue/edit.scala.html
... | ... | @@ -116,21 +116,37 @@ |
116 | 116 |
<dl id="milestoneOption" class="issue-option"> |
117 | 117 |
<dt>@Messages("milestone")</dt> |
118 | 118 |
<dd> |
119 |
- @defining(Milestone.findByProjectId(project.id)) { milestones => |
|
120 |
- @if(milestones.isEmpty()) { |
|
119 |
+ @defining(issue.milestone != null) { hasMilestone => |
|
120 |
+ @if(Milestone.findByProjectId(project.id).isEmpty()) { |
|
121 | 121 |
<a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" target="_blank" class="ybtn ybtn-success ybtn-small">@Messages("milestone.menu.new")</a> |
122 | 122 |
} else { |
123 | 123 |
<select id="milestoneId" name="milestoneId" |
124 | 124 |
data-toggle="select2" data-format="milestone" |
125 | 125 |
data-placeholder="@Messages("issue.milestone.selectDefault")"> |
126 | 126 |
<option></option> |
127 |
- <option value="-1" @if(issue.milestone == null){selected}>@Messages("issue.noMilestone")</option> |
|
128 |
- @for(milestone <- milestones){ |
|
129 |
- <option value="@milestone.id" data-state="@milestone.state" |
|
130 |
- @if(issue.milestone != null && issue.milestone.id == milestone.id){selected}> |
|
131 |
- @milestone.title |
|
127 |
+ <option value="@Milestone.NULL_MILESTONE_ID" @if(!hasMilestone){ selected }> |
|
128 |
+ @Messages("issue.noMilestone") |
|
132 | 129 |
</option> |
130 |
+ <optgroup label="@Messages("milestone.state.open")"> |
|
131 |
+ @for(milestone <- Milestone.findOpenMilestones(project.id)){ |
|
132 |
+ <option value="@milestone.id" data-state="@milestone.state" |
|
133 |
+ @if(hasMilestone && issue.milestone.id == milestone.id){ |
|
134 |
+ selected |
|
135 |
+ }> |
|
136 |
+ @milestone.title |
|
137 |
+ </option> |
|
133 | 138 |
} |
139 |
+ </optgroup> |
|
140 |
+ <optgroup label="@Messages("milestone.state.closed")"> |
|
141 |
+ @for(milestone <- Milestone.findClosedMilestones(project.id)){ |
|
142 |
+ <option value="@milestone.id" data-state="@milestone.state" |
|
143 |
+ @if(hasMilestone && issue.milestone.id == milestone.id){ |
|
144 |
+ selected |
|
145 |
+ }> |
|
146 |
+ @milestone.title |
|
147 |
+ </option> |
|
148 |
+ } |
|
149 |
+ </optgroup> |
|
134 | 150 |
</select> |
135 | 151 |
} |
136 | 152 |
} |
--- app/views/issue/partial_search.scala.html
+++ app/views/issue/partial_search.scala.html
... | ... | @@ -126,30 +126,32 @@ |
126 | 126 |
<dd> |
127 | 127 |
<select id="milestoneId" name="milestoneId" data-toggle="select2" data-format="milestone"> |
128 | 128 |
<option value="">@Messages("milestone.state.all")</option> |
129 |
- @if(param.milestoneId != null |
|
130 |
- && param.milestoneId == Milestone.NULL_MILESTONE_ID){ |
|
131 |
- <option value="@Milestone.NULL_MILESTONE_ID"> |
|
132 |
- @Messages("issue.noMilestone") |
|
133 |
- </option> |
|
134 |
- } |
|
135 |
- @for(milestone <- Milestone.findMilestones(project.id, State.OPEN, "dueDate", Direction.DESC)){ |
|
136 |
- <option value="@milestone.id" |
|
137 |
- data-state="@milestone.state" |
|
129 |
+ <option value="@Milestone.NULL_MILESTONE_ID" |
|
130 |
+ @if(param.milestoneId != null && param.milestoneId == Milestone.NULL_MILESTONE_ID){ |
|
131 |
+ selected |
|
132 |
+ }> |
|
133 |
+ @Messages("issue.noMilestone") |
|
134 |
+ </option> |
|
135 |
+ <optgroup label="@Messages("milestone.state.open")"> |
|
136 |
+ @for(milestone <- Milestone.findOpenMilestones(project.id)){ |
|
137 |
+ <option value="@milestone.id" data-state="@milestone.state" |
|
138 | 138 |
@if(param.milestoneId != null && param.milestoneId == milestone.id){ |
139 | 139 |
selected |
140 | 140 |
}> |
141 | 141 |
@milestone.title |
142 | 142 |
</option> |
143 | 143 |
} |
144 |
- @for(milestone <- Milestone.findMilestones(project.id, State.CLOSED, "dueDate", Direction.DESC)){ |
|
145 |
- <option value="@milestone.id" |
|
146 |
- data-state="@milestone.state" |
|
144 |
+ </optgroup> |
|
145 |
+ <optgroup label="@Messages("milestone.state.closed")"> |
|
146 |
+ @for(milestone <- Milestone.findClosedMilestones(project.id)){ |
|
147 |
+ <option value="@milestone.id" data-state="@milestone.state" |
|
147 | 148 |
@if(param.milestoneId != null && param.milestoneId == milestone.id){ |
148 | 149 |
selected |
149 | 150 |
}> |
150 | 151 |
@milestone.title |
151 | 152 |
</option> |
152 | 153 |
} |
154 |
+ </optgroup> |
|
153 | 155 |
</select> |
154 | 156 |
</dd> |
155 | 157 |
</dl> |
--- app/views/issue/view.scala.html
+++ app/views/issue/view.scala.html
... | ... | @@ -146,27 +146,44 @@ |
146 | 146 |
<dl> |
147 | 147 |
<dt>@Messages("milestone")</dt> |
148 | 148 |
<dd style="padding:5px 10px;"> |
149 |
- @defining(issue.milestone != null) { hasMilestone => |
|
150 |
- @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
|
151 |
- <select id="milestone" name="milestone.id" data-toggle="select2" data-format="milestone"> |
|
152 |
- <option value="-1" @if(!hasMilestone){ selected }>@Messages("issue.noMilestone")</option> |
|
153 |
- @for(milestone <- Milestone.findByProjectId(project.id)){ |
|
154 |
- <option value="@milestone.id" |
|
155 |
- @if(hasMilestone && issue.milestone.id == milestone.id){ |
|
156 |
- selected |
|
149 |
+ @if(Milestone.findByProjectId(project.id).isEmpty()){ |
|
150 |
+ <a href="@routes.MilestoneApp.newMilestoneForm(project.owner, project.name)" target="_blank" class="ybtn ybtn-success ybtn-small">@Messages("milestone.menu.new")</a> |
|
151 |
+ } else { |
|
152 |
+ @defining(issue.milestone != null) { hasMilestone => |
|
153 |
+ @if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.UPDATE)) { |
|
154 |
+ <select id="milestone" name="milestone.id" data-toggle="select2" data-format="milestone"> |
|
155 |
+ <option value="@Milestone.NULL_MILESTONE_ID" @if(!hasMilestone){ selected }> |
|
156 |
+ @Messages("issue.noMilestone") |
|
157 |
+ </option> |
|
158 |
+ <optgroup label="@Messages("milestone.state.open")"> |
|
159 |
+ @for(milestone <- Milestone.findOpenMilestones(project.id)){ |
|
160 |
+ <option value="@milestone.id" data-state="@milestone.state" |
|
161 |
+ @if(hasMilestone && issue.milestone.id == milestone.id){ |
|
162 |
+ selected |
|
163 |
+ }> |
|
164 |
+ @milestone.title |
|
165 |
+ </option> |
|
157 | 166 |
} |
158 |
- data-state="@milestone.state"> |
|
159 |
- @milestone.title |
|
160 |
- </option> |
|
161 |
- } |
|
162 |
- </select> |
|
163 |
- } else { |
|
164 |
- @if(hasMilestone){ |
|
165 |
- <a href="@routes.MilestoneApp.milestone(project.owner, project.name, issue.milestone.id)"> |
|
166 |
- @issue.milestone.title |
|
167 |
- </a> |
|
167 |
+ </optgroup> |
|
168 |
+ <optgroup label="@Messages("milestone.state.closed")"> |
|
169 |
+ @for(milestone <- Milestone.findClosedMilestones(project.id)){ |
|
170 |
+ <option value="@milestone.id" data-state="@milestone.state" |
|
171 |
+ @if(hasMilestone && issue.milestone.id == milestone.id){ |
|
172 |
+ selected |
|
173 |
+ }> |
|
174 |
+ @milestone.title |
|
175 |
+ </option> |
|
176 |
+ } |
|
177 |
+ </optgroup> |
|
178 |
+ </select> |
|
168 | 179 |
} else { |
169 |
- @Messages("issue.noMilestone") |
|
180 |
+ @if(hasMilestone){ |
|
181 |
+ <a href="@routes.MilestoneApp.milestone(project.owner, project.name, issue.milestone.id)"> |
|
182 |
+ @issue.milestone.title |
|
183 |
+ </a> |
|
184 |
+ } else { |
|
185 |
+ @Messages("issue.noMilestone") |
|
186 |
+ } |
|
170 | 187 |
} |
171 | 188 |
} |
172 | 189 |
} |
... | ... | @@ -177,7 +194,7 @@ |
177 | 194 |
@**<!-- voters -->**@ |
178 | 195 |
<dl> |
179 | 196 |
<dt>@Messages("issue.vote")</dt> |
180 |
- <dd> |
|
197 |
+ <dd style="padding:5px 10px;"> |
|
181 | 198 |
@if(isProjectResourceCreatable(User.findByLoginId(session.get("loginId")), project, ResourceType.ISSUE_COMMENT)) { |
182 | 199 |
@if(issue.isVotedBy(UserApp.currentUser())) { |
183 | 200 |
<a href="@routes.VoteApp.unvote(project.owner, project.name, issue.getNumber)" data-request-method="post" class="ybtn ybtn-success" data-toggle="tooltip" data-placement="right" data-original-title="@Messages("issue.unvote.description")">+@issue.voters.size</a> |
... | ... | @@ -222,7 +239,6 @@ |
222 | 239 |
|
223 | 240 |
@if(isAllowed(UserApp.currentUser(), issue.asResource(), Operation.WATCH)) { |
224 | 241 |
<button id="watch-button" type="button" class="ybtn" data-toggle="button" data-watching="@if(issue.getWatchers.contains(UserApp.currentUser())){true}else{false}"> |
225 |
- |
|
226 | 242 |
@if(issue.getWatchers.contains(UserApp.currentUser())) { |
227 | 243 |
@Messages("project.unwatch") |
228 | 244 |
} else { |
--- public/javascripts/common/yobi.ui.Select2.js
+++ public/javascripts/common/yobi.ui.Select2.js
... | ... | @@ -70,7 +70,7 @@ |
70 | 70 |
sMilestoneState = sMilestoneState.toLowerCase(); |
71 | 71 |
var sMilestoneStateLabel = Messages("milestone.state." + sMilestoneState); |
72 | 72 |
var sTplMilestoneItem = $("#tplSElect2FormatMilestone").text() |
73 |
- || '<div title="${name}"><span class="label milestone-state ${state}">${stateLabel}</span> ${name}</div>'; |
|
73 |
+ || '<div title="[${stateLabel}] ${name}">${name}</div>'; |
|
74 | 74 |
|
75 | 75 |
var sText = $yobi.tmpl(sTplMilestoneItem, { |
76 | 76 |
"name" : oItem.text.trim(), |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?