mirror of
https://github.com/discourse/discourse.git
synced 2026-08-18 08:55:14 -05:00
UX: Use icons as bulleted list in invite modal (#13229)
This commit is contained in:
@@ -1,11 +1,11 @@
|
|||||||
import Controller from "@ember/controller";
|
import Controller from "@ember/controller";
|
||||||
import { action } from "@ember/object";
|
import { action } from "@ember/object";
|
||||||
import { equal } from "@ember/object/computed";
|
import { empty, notEmpty } from "@ember/object/computed";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { extractError } from "discourse/lib/ajax-error";
|
import { extractError } from "discourse/lib/ajax-error";
|
||||||
|
import { getNativeContact } from "discourse/lib/pwa-utils";
|
||||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||||
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
import ModalFunctionality from "discourse/mixins/modal-functionality";
|
||||||
import { getNativeContact } from "discourse/lib/pwa-utils";
|
|
||||||
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";
|
||||||
@@ -24,7 +24,8 @@ export default Controller.extend(
|
|||||||
limitToEmail: false,
|
limitToEmail: false,
|
||||||
autogenerated: false,
|
autogenerated: false,
|
||||||
|
|
||||||
type: "link",
|
isLink: empty("buffered.email"),
|
||||||
|
isEmail: notEmpty("buffered.email"),
|
||||||
|
|
||||||
onShow() {
|
onShow() {
|
||||||
Group.findAll().then((groups) => {
|
Group.findAll().then((groups) => {
|
||||||
@@ -52,10 +53,7 @@ export default Controller.extend(
|
|||||||
},
|
},
|
||||||
|
|
||||||
setInvite(invite) {
|
setInvite(invite) {
|
||||||
this.setProperties({
|
this.set("invite", invite);
|
||||||
invite,
|
|
||||||
type: invite.email ? "email" : "link",
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setAutogenerated(value) {
|
setAutogenerated(value) {
|
||||||
@@ -70,7 +68,7 @@ export default Controller.extend(
|
|||||||
const data = { ...this.buffered.buffer };
|
const data = { ...this.buffered.buffer };
|
||||||
|
|
||||||
if (data.groupIds !== undefined) {
|
if (data.groupIds !== undefined) {
|
||||||
data.group_ids = data.groupIds;
|
data.group_ids = data.groupIds.length > 0 ? data.groupIds : "";
|
||||||
delete data.groupIds;
|
delete data.groupIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,13 +78,12 @@ export default Controller.extend(
|
|||||||
delete data.topicTitle;
|
delete data.topicTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.type === "link") {
|
if (this.isLink) {
|
||||||
if (this.buffered.get("email")) {
|
if (this.invite.email) {
|
||||||
data.email = "";
|
data.email = data.custom_message = "";
|
||||||
data.custom_message = "";
|
|
||||||
}
|
}
|
||||||
} else if (this.type === "email") {
|
} else if (this.isEmail) {
|
||||||
if (this.buffered.get("max_redemptions_allowed") > 1) {
|
if (this.invite.max_redemptions_allowed > 1) {
|
||||||
data.max_redemptions_allowed = 1;
|
data.max_redemptions_allowed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +103,7 @@ export default Controller.extend(
|
|||||||
this.rollbackBuffer();
|
this.rollbackBuffer();
|
||||||
this.setAutogenerated(opts.autogenerated);
|
this.setAutogenerated(opts.autogenerated);
|
||||||
if (!this.autogenerated) {
|
if (!this.autogenerated) {
|
||||||
if (this.type === "email" && opts.sendEmail) {
|
if (this.isEmail && opts.sendEmail) {
|
||||||
this.send("closeModal");
|
this.send("closeModal");
|
||||||
} else {
|
} else {
|
||||||
this.appEvents.trigger("modal-body:flash", {
|
this.appEvents.trigger("modal-body:flash", {
|
||||||
@@ -126,9 +123,6 @@ export default Controller.extend(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
isLink: equal("type", "link"),
|
|
||||||
isEmail: equal("type", "email"),
|
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
"currentUser.staff",
|
"currentUser.staff",
|
||||||
"siteSettings.invite_link_max_redemptions_limit",
|
"siteSettings.invite_link_max_redemptions_limit",
|
||||||
@@ -156,46 +150,16 @@ export default Controller.extend(
|
|||||||
return staff || groups.any((g) => g.owner);
|
return staff || groups.any((g) => g.owner);
|
||||||
},
|
},
|
||||||
|
|
||||||
@discourseComputed("type", "buffered.email")
|
@discourseComputed("currentUser.staff", "isEmail", "canInviteToGroup")
|
||||||
disabled(type, email) {
|
hasAdvanced(staff, isEmail, canInviteToGroup) {
|
||||||
if (type === "email") {
|
return staff || isEmail || canInviteToGroup;
|
||||||
return !email;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("buffered.hasBufferedChanges", "invite.email", "type")
|
|
||||||
changed(hasBufferedChanges, inviteEmail, type) {
|
|
||||||
return hasBufferedChanges || (inviteEmail ? "email" : "link") !== type;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("currentUser.staff", "type")
|
|
||||||
hasAdvanced(staff, type) {
|
|
||||||
return staff || type === "email";
|
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
copied() {
|
copied() {
|
||||||
if (this.type === "email" && !this.buffered.get("email")) {
|
|
||||||
return this.appEvents.trigger("modal-body:flash", {
|
|
||||||
text: I18n.t("user.invited.invite.blank_email"),
|
|
||||||
messageClass: "error",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.save({ sendEmail: false, copy: true });
|
this.save({ sendEmail: false, copy: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
|
||||||
toggleLimitToEmail() {
|
|
||||||
const limitToEmail = !this.limitToEmail;
|
|
||||||
this.setProperties({
|
|
||||||
limitToEmail,
|
|
||||||
type: limitToEmail ? "email" : "link",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
saveInvite(sendEmail) {
|
saveInvite(sendEmail) {
|
||||||
this.appEvents.trigger("modal-body:clearFlash");
|
this.appEvents.trigger("modal-body:clearFlash");
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<label for="choose-topic-title">{{i18n labelText}}</label>
|
<label for="choose-topic-title">
|
||||||
|
{{#if labelIcon}}{{d-icon labelIcon}}{{/if}}{{i18n labelText}}
|
||||||
|
</label>
|
||||||
|
|
||||||
{{text-field value=topicTitle placeholderKey="choose_topic.title.placeholder" id="choose-topic-title"}}
|
{{text-field value=topicTitle placeholderKey="choose_topic.title.placeholder" id="choose-topic-title"}}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<div class="future-date-input">
|
<div class="future-date-input">
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class={{labelClasses}}>{{displayLabel}}</label>
|
<label class={{labelClasses}}>
|
||||||
|
{{#if displayLabelIcon}}{{d-icon displayLabelIcon}}{{/if}}{{displayLabel}}
|
||||||
|
</label>
|
||||||
{{future-date-input-selector
|
{{future-date-input-selector
|
||||||
minimumResultsForSearch=-1
|
minimumResultsForSearch=-1
|
||||||
statusType=statusType
|
statusType=statusType
|
||||||
|
|||||||
@@ -13,33 +13,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>{{expiresAtLabel}}</p>
|
<div class="input-group input-expires-at">
|
||||||
|
<label>{{d-icon "far-clock"}}{{expiresAtLabel}}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-group invite-type">
|
<div class="input-group input-email">
|
||||||
{{input type="checkbox" id="invite-type" checked=limitToEmail click=(action "toggleLimitToEmail")}}
|
<label for="invite-email">{{d-icon "envelope"}}{{i18n "user.invited.invite.restrict_email"}}</label>
|
||||||
<label for="invite-type">{{i18n "user.invited.invite.restrict_email"}}</label>
|
<div class="invite-input-with-button">
|
||||||
|
{{input
|
||||||
{{#if isEmail}}
|
id="invite-email"
|
||||||
<div class="invite-input-with-button">
|
value=buffered.email
|
||||||
{{input
|
placeholderKey="topic.invite_reply.email_placeholder"
|
||||||
id="invite-email"
|
}}
|
||||||
value=buffered.email
|
{{#if capabilities.hasContactPicker}}
|
||||||
placeholderKey="topic.invite_reply.email_placeholder"
|
{{d-button
|
||||||
|
icon="address-book"
|
||||||
|
action=(action "searchContact")
|
||||||
|
class="btn-primary open-contact-picker"
|
||||||
}}
|
}}
|
||||||
{{#if capabilities.hasContactPicker}}
|
{{/if}}
|
||||||
{{d-button
|
</div>
|
||||||
icon="address-book"
|
|
||||||
action=(action "searchContact")
|
|
||||||
class="btn-primary open-contact-picker"
|
|
||||||
}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if isLink}}
|
{{#if isLink}}
|
||||||
<div class="input-group invite-max-redemptions">
|
<div class="input-group invite-max-redemptions">
|
||||||
<label for="invite-max-redemptions">{{i18n "user.invited.invite.max_redemptions_allowed"}}</label>
|
<label for="invite-max-redemptions">{{d-icon "users"}}{{i18n "user.invited.invite.max_redemptions_allowed"}}</label>
|
||||||
{{input
|
{{input
|
||||||
id="invite-max-redemptions"
|
id="invite-max-redemptions"
|
||||||
type="number"
|
type="number"
|
||||||
@@ -50,10 +48,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isEmail}}
|
{{#if showAdvanced}}
|
||||||
{{#if showAdvanced}}
|
{{#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}}
|
||||||
@@ -66,12 +64,13 @@
|
|||||||
selectedTopicId=buffered.topicId
|
selectedTopicId=buffered.topicId
|
||||||
topicTitle=buffered.topicTitle
|
topicTitle=buffered.topicTitle
|
||||||
additionalFilters="status:public"
|
additionalFilters="status:public"
|
||||||
|
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">
|
<div class="input-group invite-to-topic">
|
||||||
<label for="invite-topic">{{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"
|
||||||
@@ -85,7 +84,7 @@
|
|||||||
{{#if showAdvanced}}
|
{{#if showAdvanced}}
|
||||||
{{#if canInviteToGroup}}
|
{{#if canInviteToGroup}}
|
||||||
<div class="input-group invite-to-groups">
|
<div class="input-group invite-to-groups">
|
||||||
<label>{{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
|
||||||
@@ -100,6 +99,7 @@
|
|||||||
{{#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"
|
||||||
displayLabel=(i18n "user.invited.invite.expires_at")
|
displayLabel=(i18n "user.invited.invite.expires_at")
|
||||||
includeDateTime=true
|
includeDateTime=true
|
||||||
includeMidFuture=true
|
includeMidFuture=true
|
||||||
@@ -118,7 +118,6 @@
|
|||||||
label="user.invited.invite.save_invite"
|
label="user.invited.invite.save_invite"
|
||||||
class="btn-primary save-invite"
|
class="btn-primary save-invite"
|
||||||
action=(action "saveInvite")
|
action=(action "saveInvite")
|
||||||
disabled=disabled
|
|
||||||
}}
|
}}
|
||||||
|
|
||||||
{{#if isEmail}}
|
{{#if isEmail}}
|
||||||
@@ -127,7 +126,6 @@
|
|||||||
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)
|
||||||
disabled=disabled
|
|
||||||
}}
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
import { click, fillIn, visit } from "@ember/test-helpers";
|
||||||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
import I18n from "I18n";
|
|
||||||
|
|
||||||
acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
||||||
let deleted;
|
let deleted;
|
||||||
@@ -23,7 +22,16 @@ acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
server.post("/invites", () => helper.response(inviteData));
|
server.post("/invites", () => helper.response(inviteData));
|
||||||
server.put("/invites/1", () => helper.response(inviteData));
|
server.put("/invites/1", (request) => {
|
||||||
|
const data = helper.parsePostData(request.requestBody);
|
||||||
|
if (data.email === "error") {
|
||||||
|
return helper.response(422, {
|
||||||
|
errors: ["error isn't a valid email address."],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return helper.response(inviteData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
server.delete("/invites", () => {
|
server.delete("/invites", () => {
|
||||||
deleted = true;
|
deleted = true;
|
||||||
@@ -95,11 +103,11 @@ acceptance("Invites - Create & Edit Invite Modal", function (needs) {
|
|||||||
await visit("/u/eviltrout/invited/pending");
|
await visit("/u/eviltrout/invited/pending");
|
||||||
await click(".invite-controls .btn:first-child");
|
await click(".invite-controls .btn:first-child");
|
||||||
|
|
||||||
await click("#invite-type");
|
await fillIn("#invite-email", "error");
|
||||||
await click(".invite-link .btn");
|
await click(".invite-link .btn");
|
||||||
assert.equal(
|
assert.equal(
|
||||||
find("#modal-alert").text(),
|
find("#modal-alert").text(),
|
||||||
I18n.t("user.invited.invite.blank_email")
|
"error isn't a valid email address."
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -172,11 +180,9 @@ acceptance("Invites - Email Invites", function (needs) {
|
|||||||
await visit("/u/eviltrout/invited/pending");
|
await visit("/u/eviltrout/invited/pending");
|
||||||
await click(".invite-controls .btn:first-child");
|
await click(".invite-controls .btn:first-child");
|
||||||
|
|
||||||
await click("#invite-type");
|
|
||||||
|
|
||||||
assert.ok(find("#invite-email").length, "shows email field");
|
assert.ok(find("#invite-email").length, "shows email field");
|
||||||
|
|
||||||
await fillIn("#invite-email", "test@example.com");
|
await fillIn("#invite-email", "test@example.com");
|
||||||
|
|
||||||
assert.ok(find(".save-invite").length, "shows save without email button");
|
assert.ok(find(".save-invite").length, "shows save without email button");
|
||||||
await click(".save-invite");
|
await click(".save-invite");
|
||||||
assert.ok(
|
assert.ok(
|
||||||
|
|||||||
@@ -832,7 +832,7 @@
|
|||||||
.create-invite-modal,
|
.create-invite-modal,
|
||||||
.share-topic-modal {
|
.share-topic-modal {
|
||||||
.input-group {
|
.input-group {
|
||||||
margin-bottom: 1em;
|
margin-bottom: 0.5em;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@@ -841,24 +841,24 @@
|
|||||||
input[type="text"] {
|
input[type="text"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.invite-type {
|
textarea#invite-message,
|
||||||
input[type="checkbox"] {
|
&.invite-to-topic input[type="text"],
|
||||||
display: inline;
|
.group-chooser,
|
||||||
vertical-align: middle;
|
.user-chooser,
|
||||||
margin-top: -1px;
|
.future-date-input-selector {
|
||||||
|
margin-left: 25px;
|
||||||
|
width: calc(100% - 25px);
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
&.invite-to-topic input[type="radio"] {
|
||||||
display: inline-block;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.group-chooser,
|
label .d-icon {
|
||||||
.user-chooser,
|
color: var(--primary-medium);
|
||||||
.future-date-input-selector {
|
margin-right: 10px;
|
||||||
width: 100%;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group input[type="text"],
|
.input-group input[type="text"],
|
||||||
@@ -868,10 +868,6 @@
|
|||||||
height: 34px;
|
height: 34px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group .btn {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invite-input-with-button {
|
.invite-input-with-button {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
@@ -880,6 +876,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group.input-expires-at,
|
||||||
|
.input-group.input-email,
|
||||||
|
.input-group.invite-max-redemptions {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
width: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.future-date-input {
|
.future-date-input {
|
||||||
.date-picker-wrapper {
|
.date-picker-wrapper {
|
||||||
input {
|
input {
|
||||||
@@ -900,6 +906,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input-group.input-email {
|
||||||
|
align-items: baseline;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-input-with-button {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.invite-max-redemptions {
|
.invite-max-redemptions {
|
||||||
label {
|
label {
|
||||||
display: inline;
|
display: inline;
|
||||||
@@ -910,6 +935,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.invite-to-topic {
|
||||||
|
#choose-topic-title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.show-advanced {
|
.show-advanced {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
|
|||||||
@@ -1534,7 +1534,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}."
|
||||||
@@ -1542,21 +1542,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 the invite to one email address"
|
restrict_email: "Restrict to one email address"
|
||||||
|
|
||||||
max_redemptions_allowed: "Max number of uses:"
|
max_redemptions_allowed: "Max uses"
|
||||||
|
|
||||||
add_to_groups: "Add to groups:"
|
add_to_groups: "Add to groups"
|
||||||
invite_to_topic: "Send to topic on first login:"
|
invite_to_topic: "Arrive at this 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"
|
||||||
save_invite: "Save Invite"
|
save_invite: "Save Invite"
|
||||||
|
|
||||||
invite_saved: "Invite saved."
|
invite_saved: "Invite saved."
|
||||||
invite_copied: "Invite link copied."
|
invite_copied: "Invite link copied."
|
||||||
blank_email: "Invite link not copied. Email address is required."
|
|
||||||
|
|
||||||
bulk_invite:
|
bulk_invite:
|
||||||
none: "No invitations to display on this page."
|
none: "No invitations to display on this page."
|
||||||
|
|||||||
Reference in New Issue
Block a user