mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Improved create invite modal (#14151)
* FEATURE: Always show advanced invite options The UI is more simple and more efficient than how it was when the advanced options toggle was introduced. It does not make sense to keep it anymore. * UX: Minor copy edits * UX: Merge expire invite controls There were two controls in the create invite modal. One was a static text that displayed how much time is left until the invite expires. The other one was a datetime selector that set the time the invite expires. This commit merges the two controls in a single one: staff users will continue to see the datetime selector without the static text and regular users will only see the static text because they cannot set when the invite expires. * UX: Remove invite link It should only be visible after the invite was created.
This commit is contained in:
parent
ed2c3ebd71
commit
6ae065f9cd
@ -1,8 +1,10 @@
|
|||||||
import { and, empty, equal } from "@ember/object/computed";
|
|
||||||
import { action } from "@ember/object";
|
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { FORMAT } from "select-kit/components/future-date-input-selector";
|
import { action } from "@ember/object";
|
||||||
|
import { and, empty, equal } from "@ember/object/computed";
|
||||||
|
import { CLOSE_STATUS_TYPE } from "discourse/controllers/edit-topic-timer";
|
||||||
|
import buildTimeframes from "discourse/lib/timeframes-builder";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { FORMAT } from "select-kit/components/future-date-input-selector";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
selection: null,
|
selection: null,
|
||||||
@ -20,12 +22,17 @@ export default Component.extend({
|
|||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
if (this.input) {
|
if (this.input) {
|
||||||
const datetime = moment(this.input);
|
const dateTime = moment(this.input);
|
||||||
this.setProperties({
|
const closestTimeframe = this.findClosestTimeframe(dateTime);
|
||||||
selection: "pick_date_and_time",
|
if (closestTimeframe) {
|
||||||
_date: datetime.format("YYYY-MM-DD"),
|
this.set("selection", closestTimeframe.id);
|
||||||
_time: datetime.format("HH:mm"),
|
} else {
|
||||||
});
|
this.setProperties({
|
||||||
|
selection: "pick_date_and_time",
|
||||||
|
_date: dateTime.format("YYYY-MM-DD"),
|
||||||
|
_time: dateTime.format("HH:mm"),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -64,4 +71,31 @@ export default Component.extend({
|
|||||||
this.attrs.onChangeInput && this.attrs.onChangeInput(null);
|
this.attrs.onChangeInput && this.attrs.onChangeInput(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
findClosestTimeframe(dateTime) {
|
||||||
|
const now = moment();
|
||||||
|
|
||||||
|
const futureDateInputSelectorOptions = {
|
||||||
|
now,
|
||||||
|
day: now.day(),
|
||||||
|
includeWeekend: this.includeWeekend,
|
||||||
|
includeMidFuture: this.includeMidFuture || true,
|
||||||
|
includeFarFuture: this.includeFarFuture,
|
||||||
|
includeDateTime: this.includeDateTime,
|
||||||
|
canScheduleNow: this.includeNow || false,
|
||||||
|
canScheduleToday: 24 - now.hour() > 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
return buildTimeframes(futureDateInputSelectorOptions).find((tf) => {
|
||||||
|
const tfDateTime = tf.when(
|
||||||
|
moment(),
|
||||||
|
this.statusType !== CLOSE_STATUS_TYPE ? 8 : 18
|
||||||
|
);
|
||||||
|
|
||||||
|
if (tfDateTime) {
|
||||||
|
const diff = tfDateTime.diff(dateTime);
|
||||||
|
return 0 <= diff && diff < 60 * 1000;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,7 @@ import ModalFunctionality from "discourse/mixins/modal-functionality";
|
|||||||
import Group from "discourse/models/group";
|
import Group from "discourse/models/group";
|
||||||
import Invite from "discourse/models/invite";
|
import Invite from "discourse/models/invite";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { FORMAT } from "select-kit/components/future-date-input-selector";
|
||||||
|
|
||||||
export default Controller.extend(
|
export default Controller.extend(
|
||||||
ModalFunctionality,
|
ModalFunctionality,
|
||||||
@ -16,13 +17,16 @@ export default Controller.extend(
|
|||||||
{
|
{
|
||||||
allGroups: null,
|
allGroups: null,
|
||||||
|
|
||||||
|
flashText: null,
|
||||||
|
flashClass: null,
|
||||||
|
flashLink: false,
|
||||||
|
|
||||||
invite: null,
|
invite: null,
|
||||||
invites: null,
|
invites: null,
|
||||||
|
|
||||||
showAdvanced: false,
|
editing: false,
|
||||||
inviteToTopic: false,
|
inviteToTopic: false,
|
||||||
limitToEmail: false,
|
limitToEmail: false,
|
||||||
autogenerated: false,
|
|
||||||
|
|
||||||
isLink: empty("buffered.email"),
|
isLink: empty("buffered.email"),
|
||||||
isEmail: notEmpty("buffered.email"),
|
isEmail: notEmpty("buffered.email"),
|
||||||
@ -33,37 +37,33 @@ export default Controller.extend(
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
flashText: null,
|
||||||
|
flashClass: null,
|
||||||
|
flashLink: false,
|
||||||
invite: null,
|
invite: null,
|
||||||
invites: null,
|
invites: null,
|
||||||
showAdvanced: false,
|
editing: false,
|
||||||
inviteToTopic: false,
|
inviteToTopic: false,
|
||||||
limitToEmail: false,
|
limitToEmail: false,
|
||||||
autogenerated: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setInvite(Invite.create());
|
this.setInvite(Invite.create());
|
||||||
|
this.buffered.setProperties({
|
||||||
|
max_redemptions_allowed: 1,
|
||||||
|
expires_at: moment()
|
||||||
|
.add(this.siteSettings.invite_expiry_days, "days")
|
||||||
|
.format(FORMAT),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
if (this.autogenerated) {
|
this.appEvents.trigger("modal-body:clearFlash");
|
||||||
this.invite
|
|
||||||
.destroy()
|
|
||||||
.then(() => this.invites && this.invites.removeObject(this.invite));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setInvite(invite) {
|
setInvite(invite) {
|
||||||
this.set("invite", invite);
|
this.set("invite", invite);
|
||||||
},
|
},
|
||||||
|
|
||||||
setAutogenerated(value) {
|
|
||||||
if (this.invites && (this.autogenerated || !this.invite.id) && !value) {
|
|
||||||
this.invites.unshiftObject(this.invite);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set("autogenerated", value);
|
|
||||||
},
|
|
||||||
|
|
||||||
save(opts) {
|
save(opts) {
|
||||||
const data = { ...this.buffered.buffer };
|
const data = { ...this.buffered.buffer };
|
||||||
|
|
||||||
@ -101,29 +101,37 @@ export default Controller.extend(
|
|||||||
.save(data)
|
.save(data)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
this.rollbackBuffer();
|
this.rollbackBuffer();
|
||||||
this.setAutogenerated(opts.autogenerated);
|
|
||||||
|
if (
|
||||||
|
this.invites &&
|
||||||
|
!this.invites.any((i) => i.id === this.invite.id)
|
||||||
|
) {
|
||||||
|
this.invites.unshiftObject(this.invite);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.warnings) {
|
if (result.warnings) {
|
||||||
this.appEvents.trigger("modal-body:flash", {
|
this.setProperties({
|
||||||
text: result.warnings.join(","),
|
flashText: result.warnings.join(","),
|
||||||
messageClass: "warning",
|
flashClass: "warning",
|
||||||
|
flashLink: !this.editing,
|
||||||
});
|
});
|
||||||
} else if (!this.autogenerated) {
|
} else {
|
||||||
if (this.isEmail && opts.sendEmail) {
|
if (this.isEmail && opts.sendEmail) {
|
||||||
this.send("closeModal");
|
this.send("closeModal");
|
||||||
} else {
|
} else {
|
||||||
this.appEvents.trigger("modal-body:flash", {
|
this.setProperties({
|
||||||
text: opts.copy
|
flashText: I18n.t("user.invited.invite.invite_saved"),
|
||||||
? I18n.t("user.invited.invite.invite_copied")
|
flashClass: "success",
|
||||||
: I18n.t("user.invited.invite.invite_saved"),
|
flashLink: !this.editing,
|
||||||
messageClass: "success",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
this.appEvents.trigger("modal-body:flash", {
|
this.setProperties({
|
||||||
text: extractError(e),
|
flashText: extractError(e),
|
||||||
messageClass: "error",
|
flashClass: "error",
|
||||||
|
flashLink: false,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -155,11 +163,6 @@ export default Controller.extend(
|
|||||||
return staff || groups.any((g) => g.owner);
|
return staff || groups.any((g) => g.owner);
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("currentUser.staff", "isEmail", "canInviteToGroup")
|
|
||||||
hasAdvanced(staff, isEmail, canInviteToGroup) {
|
|
||||||
return staff || isEmail || canInviteToGroup;
|
|
||||||
},
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
copied() {
|
copied() {
|
||||||
this.save({ sendEmail: false, copy: true });
|
this.save({ sendEmail: false, copy: true });
|
||||||
@ -178,10 +181,5 @@ export default Controller.extend(
|
|||||||
this.set("buffered.email", result[0].email[0]);
|
this.set("buffered.email", result[0].email[0]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
|
||||||
toggleAdvanced() {
|
|
||||||
this.toggleProperty("showAdvanced");
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -128,15 +128,11 @@ export default Controller.extend(
|
|||||||
inviteUsers() {
|
inviteUsers() {
|
||||||
this.set("showNotifyUsers", false);
|
this.set("showNotifyUsers", false);
|
||||||
const controller = showModal("create-invite");
|
const controller = showModal("create-invite");
|
||||||
controller.setProperties({
|
controller.set("inviteToTopic", true);
|
||||||
showAdvanced: true,
|
|
||||||
inviteToTopic: true,
|
|
||||||
});
|
|
||||||
controller.buffered.setProperties({
|
controller.buffered.setProperties({
|
||||||
topicId: this.topic.id,
|
topicId: this.topic.id,
|
||||||
topicTitle: this.topic.title,
|
topicTitle: this.topic.title,
|
||||||
});
|
});
|
||||||
controller.save({ autogenerated: true });
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -66,7 +66,6 @@ export default Controller.extend({
|
|||||||
createInvite() {
|
createInvite() {
|
||||||
const controller = showModal("create-invite");
|
const controller = showModal("create-invite");
|
||||||
controller.set("invites", this.model.invites);
|
controller.set("invites", this.model.invites);
|
||||||
controller.save({ autogenerated: true });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@ -77,7 +76,7 @@ export default Controller.extend({
|
|||||||
@action
|
@action
|
||||||
editInvite(invite) {
|
editInvite(invite) {
|
||||||
const controller = showModal("create-invite");
|
const controller = showModal("create-invite");
|
||||||
controller.set("showAdvanced", true);
|
controller.set("editing", true);
|
||||||
controller.setInvite(invite);
|
controller.setInvite(invite);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -32,9 +32,7 @@ export default DiscourseRoute.extend({
|
|||||||
showInviteModal() {
|
showInviteModal() {
|
||||||
const model = this.modelFor("group");
|
const model = this.modelFor("group");
|
||||||
const controller = showModal("create-invite");
|
const controller = showModal("create-invite");
|
||||||
controller.set("showAdvanced", true);
|
|
||||||
controller.buffered.set("groupIds", [model.id]);
|
controller.buffered.set("groupIds", [model.id]);
|
||||||
controller.save({ autogenerated: true });
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -1,21 +1,40 @@
|
|||||||
{{#d-modal-body title=(if invite.id "user.invited.invite.edit_title" "user.invited.invite.new_title")}}
|
{{#if flashText}}
|
||||||
<form>
|
<div id="modal-alert" role="alert" class="alert alert-{{flashClass}}">
|
||||||
<div class="input-group invite-link">
|
{{#if flashLink}}
|
||||||
<label for="invite-link">{{i18n "user.invited.invite.instructions"}}</label>
|
<div class="input-group invite-link">
|
||||||
<div class="link-share-container">
|
<label for="invite-link">{{flashText}} {{i18n "user.invited.invite.instructions"}}</label>
|
||||||
{{input
|
<div class="link-share-container">
|
||||||
name="invite-link"
|
{{input
|
||||||
class="invite-link"
|
name="invite-link"
|
||||||
value=invite.link
|
class="invite-link"
|
||||||
readonly=true
|
value=invite.link
|
||||||
}}
|
readonly=true
|
||||||
{{copy-button selector="input.invite-link" copied=(action "copied")}}
|
}}
|
||||||
|
{{copy-button selector="input.invite-link" copied=(action "copied")}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{{else}}
|
||||||
|
{{flashText}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="input-group input-expires-at">
|
{{#d-modal-body title=(if editing "user.invited.invite.edit_title" "user.invited.invite.new_title")}}
|
||||||
<label>{{d-icon "far-clock"}}{{expiresAtLabel}}</label>
|
<form>
|
||||||
</div>
|
{{#if editing}}
|
||||||
|
<div class="input-group invite-link">
|
||||||
|
<label for="invite-link">{{i18n "user.invited.invite.instructions"}}</label>
|
||||||
|
<div class="link-share-container">
|
||||||
|
{{input
|
||||||
|
name="invite-link"
|
||||||
|
class="invite-link"
|
||||||
|
value=invite.link
|
||||||
|
readonly=true
|
||||||
|
}}
|
||||||
|
{{copy-button selector="input.invite-link" copied=(action "copied")}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="input-group input-email">
|
<div class="input-group input-email">
|
||||||
<label for="invite-email">{{d-icon "envelope"}}{{i18n "user.invited.invite.restrict_email"}}</label>
|
<label for="invite-email">{{d-icon "envelope"}}{{i18n "user.invited.invite.restrict_email"}}</label>
|
||||||
@ -48,66 +67,64 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showAdvanced}}
|
{{#if isEmail}}
|
||||||
{{#if isEmail}}
|
<div class="input-group invite-custom-message">
|
||||||
<div class="input-group invite-custom-message">
|
<label for="invite-message">{{i18n "user.invited.invite.custom_message"}}</label>
|
||||||
<label for="invite-message">{{d-icon "envelope"}}{{i18n "user.invited.invite.custom_message"}}</label>
|
{{textarea id="invite-message" value=buffered.custom_message}}
|
||||||
{{textarea id="invite-message" value=buffered.custom_message}}
|
</div>
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showAdvanced}}
|
{{#if currentUser.staff}}
|
||||||
{{#if currentUser.staff}}
|
<div class="input-group invite-to-topic">
|
||||||
<div class="input-group invite-to-topic">
|
{{choose-topic
|
||||||
{{choose-topic
|
selectedTopicId=buffered.topicId
|
||||||
selectedTopicId=buffered.topicId
|
topicTitle=buffered.topicTitle
|
||||||
topicTitle=buffered.topicTitle
|
additionalFilters="status:public"
|
||||||
additionalFilters="status:public"
|
labelIcon="hand-point-right"
|
||||||
labelIcon="hand-point-right"
|
label="user.invited.invite.invite_to_topic"
|
||||||
label="user.invited.invite.invite_to_topic"
|
}}
|
||||||
}}
|
</div>
|
||||||
</div>
|
{{else if buffered.topicTitle}}
|
||||||
{{else if buffered.topicTitle}}
|
<div class="input-group invite-to-topic">
|
||||||
<div class="input-group invite-to-topic">
|
<label for="invite-topic">{{d-icon "hand-point-right"}}{{i18n "user.invited.invite.invite_to_topic"}}</label>
|
||||||
<label for="invite-topic">{{d-icon "hand-point-right"}}{{i18n "user.invited.invite.invite_to_topic"}}</label>
|
{{input
|
||||||
{{input
|
name="invite-topic"
|
||||||
name="invite-topic"
|
class="invite-topic"
|
||||||
class="invite-topic"
|
value=buffered.topicTitle
|
||||||
value=buffered.topicTitle
|
readonly=true
|
||||||
readonly=true
|
}}
|
||||||
}}
|
</div>
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showAdvanced}}
|
{{#if canInviteToGroup}}
|
||||||
{{#if canInviteToGroup}}
|
<div class="input-group invite-to-groups">
|
||||||
<div class="input-group invite-to-groups">
|
<label>{{d-icon "users"}}{{i18n "user.invited.invite.add_to_groups"}}</label>
|
||||||
<label>{{d-icon "users"}}{{i18n "user.invited.invite.add_to_groups"}}</label>
|
{{group-chooser
|
||||||
{{group-chooser
|
content=allGroups
|
||||||
content=allGroups
|
value=buffered.groupIds
|
||||||
value=buffered.groupIds
|
labelProperty="name"
|
||||||
labelProperty="name"
|
onChange=(action (mut buffered.groupIds))
|
||||||
onChange=(action (mut buffered.groupIds))
|
}}
|
||||||
}}
|
</div>
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if showAdvanced}}
|
{{#if currentUser.staff}}
|
||||||
{{#if currentUser.staff}}
|
<div class="input-group invite-expires-at">
|
||||||
<div class="input-group invite-expires-at">
|
{{future-date-input
|
||||||
{{future-date-input
|
displayLabelIcon="far-clock"
|
||||||
displayLabelIcon="far-clock"
|
displayLabel=(i18n "user.invited.invite.expires_at")
|
||||||
displayLabel=(i18n "user.invited.invite.expires_at")
|
statusType="close"
|
||||||
includeDateTime=true
|
includeDateTime=true
|
||||||
includeMidFuture=true
|
includeMidFuture=true
|
||||||
clearable=true
|
clearable=true
|
||||||
onChangeInput=(action (mut buffered.expires_at))
|
input=buffered.expires_at
|
||||||
}}
|
onChangeInput=(action (mut buffered.expires_at))
|
||||||
</div>
|
}}
|
||||||
{{/if}}
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="input-group input-expires-at">
|
||||||
|
<label>{{d-icon "far-clock"}}{{expiresAtLabel}}</label>
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</form>
|
</form>
|
||||||
{{/d-modal-body}}
|
{{/d-modal-body}}
|
||||||
@ -120,21 +137,12 @@
|
|||||||
action=(action "saveInvite")
|
action=(action "saveInvite")
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{#if isEmail}}
|
{{d-button
|
||||||
{{d-button
|
icon="envelope"
|
||||||
icon="envelope"
|
label=(if invite.emailed "user.invited.reinvite" "user.invited.invite.send_invite_email")
|
||||||
label=(if invite.emailed "user.invited.reinvite" "user.invited.invite.send_invite_email")
|
class="btn-primary send-invite"
|
||||||
class="btn-primary send-invite"
|
action=(action "saveInvite" true)
|
||||||
action=(action "saveInvite" true)
|
title=(unless isEmail "user.invited.invite.send_invite_email_instructions")
|
||||||
}}
|
disabled=(not isEmail)
|
||||||
{{/if}}
|
}}
|
||||||
|
|
||||||
{{#if hasAdvanced}}
|
|
||||||
{{d-button
|
|
||||||
action=(action "toggleAdvanced")
|
|
||||||
class="btn-default show-advanced"
|
|
||||||
icon="cog"
|
|
||||||
title=(if showAdvanced "user.invited.invite.hide_advanced" "user.invited.invite.show_advanced")
|
|
||||||
}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,15 +4,12 @@ import {
|
|||||||
count,
|
count,
|
||||||
exists,
|
exists,
|
||||||
fakeTime,
|
fakeTime,
|
||||||
query,
|
|
||||||
queryAll,
|
queryAll,
|
||||||
} from "discourse/tests/helpers/qunit-helpers";
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
import { test } from "qunit";
|
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
import { test } from "qunit";
|
||||||
|
|
||||||
acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
||||||
let deleted;
|
|
||||||
|
|
||||||
needs.user();
|
needs.user();
|
||||||
needs.pretender((server, helper) => {
|
needs.pretender((server, helper) => {
|
||||||
const inviteData = {
|
const inviteData = {
|
||||||
@ -42,30 +39,17 @@ acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.delete("/invites", () => {
|
server.delete("/invites", () => {
|
||||||
deleted = true;
|
|
||||||
return helper.response({});
|
return helper.response({});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
needs.hooks.beforeEach(() => {
|
|
||||||
deleted = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
test("basic functionality", async function (assert) {
|
test("basic functionality", async function (assert) {
|
||||||
await visit("/u/eviltrout/invited/pending");
|
await visit("/u/eviltrout/invited/pending");
|
||||||
await click(".user-invite-buttons .btn:first-child");
|
await click(".user-invite-buttons .btn:first-child");
|
||||||
assert.strictEqual(
|
|
||||||
query("input.invite-link").value,
|
|
||||||
"http://example.com/invites/52641ae8878790bc7b79916247cfe6ba",
|
|
||||||
"shows an invite link when modal is opened"
|
|
||||||
);
|
|
||||||
|
|
||||||
await click(".modal-footer .show-advanced");
|
await assert.ok(exists(".invite-to-groups"));
|
||||||
await assert.ok(exists(".invite-to-groups"), "shows advanced options");
|
await assert.ok(exists(".invite-to-topic"));
|
||||||
await assert.ok(exists(".invite-to-topic"), "shows advanced options");
|
await assert.ok(exists(".invite-expires-at"));
|
||||||
await assert.ok(exists(".invite-expires-at"), "shows advanced options");
|
|
||||||
|
|
||||||
await click(".modal-close");
|
|
||||||
assert.ok(deleted, "deletes the invite if not saved");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("saving", async function (assert) {
|
test("saving", async function (assert) {
|
||||||
@ -81,31 +65,14 @@ acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
|||||||
1,
|
1,
|
||||||
"adds invite to list after saving"
|
"adds invite to list after saving"
|
||||||
);
|
);
|
||||||
|
|
||||||
await click(".modal-close");
|
|
||||||
assert.notOk(deleted, "does not delete invite on close");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("copying saves invite", async function (assert) {
|
test("copying saves invite", async function (assert) {
|
||||||
await visit("/u/eviltrout/invited/pending");
|
await visit("/u/eviltrout/invited/pending");
|
||||||
await click(".user-invite-buttons .btn:first-child");
|
await click(".user-invite-buttons .btn:first-child");
|
||||||
|
|
||||||
await click(".invite-link .btn");
|
await click(".save-invite");
|
||||||
|
assert.ok(exists(".invite-link .btn"));
|
||||||
await click(".modal-close");
|
|
||||||
assert.notOk(deleted, "does not delete invite on close");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("copying an email invite without an email shows error message", async function (assert) {
|
|
||||||
await visit("/u/eviltrout/invited/pending");
|
|
||||||
await click(".user-invite-buttons .btn:first-child");
|
|
||||||
|
|
||||||
await fillIn("#invite-email", "error");
|
|
||||||
await click(".invite-link .btn");
|
|
||||||
assert.strictEqual(
|
|
||||||
query("#modal-alert").innerText,
|
|
||||||
"error isn't a valid email address."
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -159,7 +126,10 @@ acceptance("Invites - Email Invites", function (needs) {
|
|||||||
groups: [],
|
groups: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
server.post("/invites", () => helper.response(inviteData));
|
server.post("/invites", (request) => {
|
||||||
|
lastRequest = request;
|
||||||
|
return helper.response(inviteData);
|
||||||
|
});
|
||||||
|
|
||||||
server.put("/invites/1", (request) => {
|
server.put("/invites/1", (request) => {
|
||||||
lastRequest = request;
|
lastRequest = request;
|
||||||
@ -232,7 +202,6 @@ acceptance(
|
|||||||
await visit("/u/eviltrout/invited/pending");
|
await visit("/u/eviltrout/invited/pending");
|
||||||
|
|
||||||
await click(".user-invite-buttons .btn:first-child");
|
await click(".user-invite-buttons .btn:first-child");
|
||||||
await click(".modal-footer .show-advanced");
|
|
||||||
await click(".future-date-input-selector-header");
|
await click(".future-date-input-selector-header");
|
||||||
|
|
||||||
const options = Array.from(
|
const options = Array.from(
|
||||||
|
@ -182,9 +182,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-advanced {
|
.invite-custom-message {
|
||||||
margin-left: auto;
|
label {
|
||||||
margin-right: 0;
|
margin-left: 1.75em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group {
|
.input-group {
|
||||||
@ -198,5 +199,16 @@
|
|||||||
margin-left: 1.75em;
|
margin-left: 1.75em;
|
||||||
width: calc(100% - 1.75em);
|
width: calc(100% - 1.75em);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.future-date-input-date-picker,
|
||||||
|
.future-date-input-time-picker {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0em 0em 0em 1.75em;
|
||||||
|
width: calc(50% - 2em);
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,11 @@
|
|||||||
.create-invite-modal,
|
.create-invite-modal,
|
||||||
.create-invite-bulk-modal,
|
.create-invite-bulk-modal,
|
||||||
.share-topic-modal {
|
.share-topic-modal {
|
||||||
|
&.modal .modal-body {
|
||||||
|
margin: 1em;
|
||||||
|
padding: unset;
|
||||||
|
}
|
||||||
|
|
||||||
.modal-inner-container {
|
.modal-inner-container {
|
||||||
width: 40em; // scale with user font-size
|
width: 40em; // scale with user font-size
|
||||||
max-width: 100vw; // prevent overflow if user font-size is enourmous
|
max-width: 100vw; // prevent overflow if user font-size is enourmous
|
||||||
|
@ -1613,7 +1613,7 @@ en:
|
|||||||
new_title: "Create Invite"
|
new_title: "Create Invite"
|
||||||
edit_title: "Edit Invite"
|
edit_title: "Edit Invite"
|
||||||
|
|
||||||
instructions: "Share this link to instantly grant access to this site"
|
instructions: "Share this link to instantly grant access to this site:"
|
||||||
copy_link: "copy link"
|
copy_link: "copy link"
|
||||||
expires_in_time: "Expires in %{time}"
|
expires_in_time: "Expires in %{time}"
|
||||||
expired_at_time: "Expired at %{time}"
|
expired_at_time: "Expired at %{time}"
|
||||||
@ -1621,20 +1621,20 @@ en:
|
|||||||
show_advanced: "Show Advanced Options"
|
show_advanced: "Show Advanced Options"
|
||||||
hide_advanced: "Hide Advanced Options"
|
hide_advanced: "Hide Advanced Options"
|
||||||
|
|
||||||
restrict_email: "Restrict to one email address"
|
restrict_email: "Restrict to email"
|
||||||
|
|
||||||
max_redemptions_allowed: "Max uses"
|
max_redemptions_allowed: "Max uses"
|
||||||
|
|
||||||
add_to_groups: "Add to groups"
|
add_to_groups: "Add to groups"
|
||||||
invite_to_topic: "Arrive at this topic"
|
invite_to_topic: "Arrive at topic"
|
||||||
expires_at: "Expire after"
|
expires_at: "Expire after"
|
||||||
custom_message: "Optional personal message"
|
custom_message: "Optional personal message"
|
||||||
|
|
||||||
send_invite_email: "Save and Send Email"
|
send_invite_email: "Save and Send Email"
|
||||||
|
send_invite_email_instructions: "Restrict invite to email to send an invite email"
|
||||||
save_invite: "Save Invite"
|
save_invite: "Save Invite"
|
||||||
|
|
||||||
invite_saved: "Invite saved."
|
invite_saved: "Invite saved."
|
||||||
invite_copied: "Invite link copied."
|
|
||||||
|
|
||||||
bulk_invite:
|
bulk_invite:
|
||||||
none: "No invitations to display on this page."
|
none: "No invitations to display on this page."
|
||||||
|
@ -586,6 +586,7 @@ users:
|
|||||||
default: true
|
default: true
|
||||||
invite_expiry_days:
|
invite_expiry_days:
|
||||||
default: 30
|
default: 30
|
||||||
|
client: true
|
||||||
max: 36500
|
max: 36500
|
||||||
invites_per_page:
|
invites_per_page:
|
||||||
client: true
|
client: true
|
||||||
|
Loading…
Reference in New Issue
Block a user