-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FEATURE: Make it possible to reassign posts on the topic level assign…
… modal This adds a new dropdown to the topic level assign modal. A topic may contain several assignments, the topic itself may be assigned and also some of the replies may be assigned. With this new dropdown, it's possible now to edit all the assignments in one go.
- Loading branch information
Showing
18 changed files
with
637 additions
and
192 deletions.
There are no files selected for viewing
36 changes: 36 additions & 0 deletions
36
assets/javascripts/discourse/components/assign-user-form.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import Component from "@glimmer/component"; | ||
import { tracked } from "@glimmer/tracking"; | ||
import { action } from "@ember/object"; | ||
import Assignment from "./assignment"; | ||
|
||
export default class AssignUserForm extends Component { | ||
@tracked showValidationErrors = false; | ||
|
||
constructor() { | ||
super(...arguments); | ||
|
||
this.args.formApi.submit = this.assign; | ||
} | ||
|
||
get assigneeIsEmpty() { | ||
return !this.args.model.username && !this.args.model.group_name; | ||
} | ||
|
||
@action | ||
async assign() { | ||
if (this.assigneeIsEmpty) { | ||
this.showValidationErrors = true; | ||
return; | ||
} | ||
|
||
await this.args.onSubmit(); | ||
} | ||
|
||
<template> | ||
<Assignment | ||
@assignment={{@model}} | ||
@onSubmit={{this.assign}} | ||
@showValidationErrors={{this.showValidationErrors}} | ||
/> | ||
</template> | ||
} |
56 changes: 0 additions & 56 deletions
56
assets/javascripts/discourse/components/assign-user-form.hbs
This file was deleted.
Oops, something went wrong.
63 changes: 0 additions & 63 deletions
63
assets/javascripts/discourse/components/assign-user-form.js
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import Component from "@glimmer/component"; | ||
import { hash } from "@ember/helper"; | ||
import { TextArea } from "@ember/legacy-built-in-components"; | ||
import { on } from "@ember/modifier"; | ||
import { action } from "@ember/object"; | ||
import { inject as service } from "@ember/service"; | ||
import icon from "discourse-common/helpers/d-icon"; | ||
import i18n from "discourse-common/helpers/i18n"; | ||
import ComboBox from "select-kit/components/combo-box"; | ||
import not from "truth-helpers/helpers/not"; | ||
import AssigneeChooser from "./assignee-chooser"; | ||
|
||
export default class Assignment extends Component { | ||
@service siteSettings; | ||
@service taskActions; | ||
|
||
get assignee() { | ||
return this.args.assignment.username || this.args.assignment.group_name; | ||
} | ||
|
||
get status() { | ||
return this.args.assignment.status || this.assignStatuses[0]; | ||
} | ||
|
||
get assignStatuses() { | ||
return this.siteSettings.assign_statuses.split("|").filter(Boolean); | ||
} | ||
|
||
get assignStatusOptions() { | ||
return this.assignStatuses.map((status) => ({ id: status, name: status })); | ||
} | ||
|
||
get assigneeIsEmpty() { | ||
return !this.args.assignment.username && !this.args.assignment.group_name; | ||
} | ||
|
||
get showAssigneeIeEmptyError() { | ||
return this.assigneeIsEmpty && this.args.showValidationErrors; | ||
} | ||
|
||
@action | ||
handleTextAreaKeydown(event) { | ||
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") { | ||
this.args.onSubmit(); | ||
} | ||
} | ||
|
||
@action | ||
markAsEdited() { | ||
this.args.assignment.isEdited = true; | ||
} | ||
|
||
@action | ||
setAssignee([newAssignee]) { | ||
if (this.taskActions.allowedGroupsForAssignment.includes(newAssignee)) { | ||
this.args.assignment.username = null; | ||
this.args.assignment.group_name = newAssignee; | ||
} else { | ||
this.args.assignment.username = newAssignee; | ||
this.args.assignment.group_name = null; | ||
} | ||
this.markAsEdited(); | ||
} | ||
|
||
@action | ||
setStatus(status) { | ||
this.args.assignment.status = status; | ||
this.markAsEdited(); | ||
} | ||
|
||
<template> | ||
<div | ||
class="control-group | ||
{{if this.showAssigneeIeEmptyError 'assignee-error'}}" | ||
> | ||
<label>{{i18n "discourse_assign.assign_modal.assignee_label"}}</label> | ||
<AssigneeChooser | ||
autocomplete="off" | ||
@id="assignee-chooser" | ||
@value={{this.assignee}} | ||
@onChange={{this.setAssignee}} | ||
@showUserStatus={{true}} | ||
@options={{hash | ||
mobilePlacementStrategy="absolute" | ||
includeGroups=true | ||
customSearchOptions=(hash | ||
assignableGroups=true | ||
defaultSearchResults=this.taskActions.suggestions | ||
) | ||
groupMembersOf=this.taskActions.allowedGroups | ||
maximum=1 | ||
tabindex=1 | ||
expandedOnInsert=(not this.assignee) | ||
caretUpIcon="search" | ||
caretDownIcon="search" | ||
}} | ||
/> | ||
|
||
{{#if this.showAssigneeIeEmptyError}} | ||
<span class="error-label"> | ||
{{icon "exclamation-triangle"}} | ||
{{i18n "discourse_assign.assign_modal.choose_assignee"}} | ||
</span> | ||
{{/if}} | ||
</div> | ||
|
||
{{#if this.siteSettings.enable_assign_status}} | ||
<div class="control-group assign-status"> | ||
<label>{{i18n "discourse_assign.assign_modal.status_label"}}</label> | ||
<ComboBox | ||
@id="assign-status" | ||
@content={{this.assignStatusOptions}} | ||
@value={{this.status}} | ||
@onChange={{this.setStatus}} | ||
/> | ||
</div> | ||
{{/if}} | ||
|
||
<div class="control-group assign-status"> | ||
<label> | ||
{{i18n "discourse_assign.assign_modal.note_label"}} <span | ||
class="label-optional" | ||
>{{i18n "discourse_assign.assign_modal.optional_label"}}</span> | ||
</label> | ||
|
||
<TextArea | ||
id="assign-modal-note" | ||
@value={{@assignment.note}} | ||
{{on "keydown" this.handleTextAreaKeydown}} | ||
{{on "input" this.markAsEdited}} | ||
/> | ||
</div> | ||
</template> | ||
} |
67 changes: 67 additions & 0 deletions
67
assets/javascripts/discourse/components/modal/edit-topic-assignments.gjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import Component from "@glimmer/component"; | ||
import { tracked } from "@glimmer/tracking"; | ||
import { action } from "@ember/object"; | ||
import { inject as service } from "@ember/service"; | ||
import DButton from "discourse/components/d-button"; | ||
import DModal from "discourse/components/d-modal"; | ||
import DModalCancel from "discourse/components/d-modal-cancel"; | ||
import { popupAjaxError } from "discourse/lib/ajax-error"; | ||
import I18n from "I18n"; | ||
import TopicAssignments from "../topic-assignments"; | ||
|
||
export default class EditTopicAssignments extends Component { | ||
@service taskActions; | ||
|
||
@tracked assignments = this.topic.assignments(); | ||
|
||
get title() { | ||
if (this.topic.isAssigned() || this.topic.hasAssignedPosts()) { | ||
return I18n.t("edit_assignments_modal.title"); | ||
} else { | ||
return I18n.t("discourse_assign.assign_modal.title"); | ||
} | ||
} | ||
|
||
get topic() { | ||
return this.args.model.topic; | ||
} | ||
|
||
@action | ||
async submit() { | ||
this.args.closeModal(); | ||
try { | ||
await this.#assign(); | ||
} catch (error) { | ||
popupAjaxError(error); | ||
} | ||
} | ||
|
||
async #assign() { | ||
for (const assignment of this.assignments) { | ||
if (assignment.isEdited) { | ||
await this.taskActions.putAssignment(assignment); | ||
} | ||
} | ||
} | ||
|
||
<template> | ||
<DModal class="assign" @title={{this.title}} @closeModal={{@closeModal}}> | ||
<:body> | ||
<TopicAssignments @assignments={{this.assignments}} /> | ||
</:body> | ||
<:footer> | ||
<DButton | ||
class="btn-primary" | ||
@action={{this.submit}} | ||
@label={{if | ||
this.model.reassign | ||
"discourse_assign.reassign.title" | ||
"discourse_assign.assign_modal.assign" | ||
}} | ||
/> | ||
|
||
<DModalCancel @close={{@closeModal}} /> | ||
</:footer> | ||
</DModal> | ||
</template> | ||
} |
Oops, something went wrong.