0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-09-19 15:21:11 +02:00
Dieser Commit ist enthalten in:
Angus McLeod 2017-10-06 10:59:02 +08:00
Ursprung 7b09410a26
Commit 9f40821db0
20 geänderte Dateien mit 526 neuen und 157 gelöschten Zeilen

Datei anzeigen

@ -1,11 +1,8 @@
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: 'wizard-custom-action',
types: ['create_topic', 'update_profile', 'send_message'], types: ['create_topic', 'update_profile', 'send_message'],
profileFields: ['name', 'username', 'email'], profileFields: ['name', 'username', 'email'],
createTopic: Ember.computed.equal('action.type', 'create_topic'), createTopic: Ember.computed.equal('action.type', 'create_topic'),
updateProfile: Ember.computed.equal('action.type', 'update_profile'), updateProfile: Ember.computed.equal('action.type', 'update_profile'),
sendMessage: Ember.computed.equal('action.type', 'send_message'), sendMessage: Ember.computed.equal('action.type', 'send_message')
test: function() {
console.log(this.get('stepFields'));
}.observes('stepFields.[]')
}); });

Datei anzeigen

@ -1,9 +1,11 @@
import { observes } from 'ember-addons/ember-computed-decorators'; import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: 'wizard-custom-field', classNames: 'wizard-custom-field',
isDropdown: Ember.computed.equal('field.type', 'dropdown'), isDropdown: Ember.computed.equal('field.type', 'dropdown'),
@on('init')
@observes('field.id')
init() { init() {
this._super(...arguments); this._super(...arguments);
@ -12,15 +14,16 @@ export default Ember.Component.extend({
} }
}, },
@observes('field.label') @computed('field.choices.[]')
setFieldId() { dropdownChoices: choices => choices,
const label = this.get('field.label');
this.set('field.id', Ember.String.underscore(label));
},
actions: { actions: {
addChoice() { addChoice() {
this.get('field.choices').pushObject(Ember.Object.create()); this.get('field.choices').pushObject(Ember.Object.create());
},
removeChoice(c) {
this.get('field.choices').removeObject(c);
} }
} }
}); });

Datei anzeigen

@ -1,26 +1,103 @@
import { default as computed } from 'ember-addons/ember-computed-decorators'; import { default as computed, on, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({ export default Ember.Component.extend({
classNames: 'wizard-custom-step', classNames: 'wizard-custom-step',
currentField: null,
currentAction: null,
@computed('step.fields.@each.id') @on('init')
allowAddAction: stepFields => stepFields.get('firstObject.id'), @observes('step.id')
setup() {
this._super(...arguments);
const fields = this.get('step.fields');
const actions = this.get('step.actions');
this.set('currentField', fields[0]);
this.set('currentAction', actions[0]);
},
@computed('step.fields.[]', 'currentField')
fieldLinks(fields, current) {
return fields.map((f) => {
if (f) {
let link = {
id: f.get('id'),
label: f.get('label')
};
let classes = 'btn';
if (current && f.get('id') === current.get('id')) {
classes += ' btn-primary';
};
link['classes'] = classes;
return link;
}
});
},
@computed('step.actions.[]', 'currentAction')
actionLinks(actions, current) {
return actions.map((a) => {
if (a) {
let link = {
id: a.get('id'),
label: a.get('label')
};
let classes = 'btn';
if (current && a.get('id') === current.get('id')) {
classes += ' btn-primary';
};
link['classes'] = classes;
return link;
}
});
},
actions: { actions: {
addField() { addField() {
this.get('step.fields').pushObject(Ember.Object.create({ id: '', label: '' })); const fields = this.get('step.fields');
const newNum = fields.length + 1;
const field = Ember.Object.create({
id: `field-${newNum}`, label: `Field ${newNum}`
});
fields.pushObject(field);
this.set('currentField', field);
}, },
addAction() { addAction() {
this.get('step.actions').pushObject(Ember.Object.create({ id: '', label: '' })); const actions = this.get('step.actions');
const newNum = actions.length + 1;
const action = Ember.Object.create({
id: `action-${newNum}`, label: `Action ${newNum}`
});
actions.pushObject(action);
this.set('currentAction', action);
}, },
removeField(field) { removeField(fieldId) {
this.get('step.fields').removeObject(field); const fields = this.get('step.fields');
fields.removeObject(fields.findBy('id', fieldId));
this.set('currentField', fields[fields.length - 1]);
}, },
removeAction(action) { removeAction(actionId) {
this.get('step.actions').removeObject(action); const actions = this.get('step.actions');
actions.removeObject(actions.findBy('id', actionId));
this.set('currentAction', actions[actions.length - 1]);
},
changeField(fieldId) {
const fields = this.get('step.fields');
this.set('currentField', fields.findBy('id', fieldId));
},
changeAction(actionId) {
const actions = this.get('step.actions');
this.set('currentAction', actions.findBy('id', actionId));
} }
} }
}); });

Datei anzeigen

@ -1,8 +1,37 @@
import { default as computed } from 'ember-addons/ember-computed-decorators';
export default Ember.Controller.extend({ export default Ember.Controller.extend({
@computed('model.steps.[]', 'currentStep')
stepLinks(steps, currentStep) {
return steps.map((s) => {
if (s) {
let link = {
id: s.get('id'),
title: s.get('title')
};
let classes = 'btn';
if (currentStep && s.get('id') === currentStep.get('id')) {
classes += ' btn-primary';
};
link['classes'] = classes;
return link;
}
});
},
@computed('model.id')
wizardUrl(wizardId) {
return window.location.origin + '/wizard/custom/' + wizardId;
},
actions: { actions: {
save() { save() {
this.get('model').save().then(() => { this.get('model').save().then(() => {
this.transitionToRoute('adminWizardsCustom'); this.send("refreshRoute");
}); });
}, },
@ -13,14 +42,27 @@ export default Ember.Controller.extend({
}, },
addStep() { addStep() {
this.get('model.steps').pushObject(Ember.Object.create({ const steps = this.get('model.steps');
const newNum = steps.length + 1;
const step = Ember.Object.create({
fields: Ember.A(), fields: Ember.A(),
actions: Ember.A() actions: Ember.A(),
})); title: `Step ${newNum}`,
id: `step-${newNum}`
});
steps.pushObject(step);
this.set('currentStep', step);
}, },
removeStep(step) { removeStep(stepId) {
this.get('model.steps').removeObject(step); const steps = this.get('model.steps');
steps.removeObject(steps.findBy('id', stepId));
},
changeStep(stepId) {
const steps = this.get('model.steps');
this.set('currentStep', steps.findBy('id', stepId));
} }
} }
}); });

Datei anzeigen

@ -8,9 +8,7 @@ const CustomWizard = Discourse.Model.extend({
}, },
@computed('name') @computed('name')
id(name) { id: (name) => name ? Ember.String.dasherize(name) : null,
return name ? Ember.String.dasherize(name) : null;
},
save() { save() {
const stepsObj = this.get('steps'); const stepsObj = this.get('steps');
@ -36,11 +34,12 @@ const CustomWizard = Discourse.Model.extend({
c.set('id', c.get('label')); c.set('id', c.get('label'));
}); });
} }
step['fields'].push(f); step['fields'].push(f);
}); });
s.actions.forEach((a) => { s.actions.forEach((a) => {
a['id'] = Ember.String.dasherize(a.label); a.set('id', Ember.String.dasherize(a.get('label')));
step['actions'].push(a); step['actions'].push(a);
}); });
@ -128,6 +127,7 @@ CustomWizard.reopenClass({
id: s.id, id: s.id,
title: s.title, title: s.title,
description: s.description, description: s.description,
banner: s.banner,
fields, fields,
actions actions
})); }));

Datei anzeigen

@ -21,7 +21,15 @@ export default Discourse.Route.extend({
}, },
setupController(controller, model) { setupController(controller, model) {
controller.set("new", this.get('new')); let props = { new: this.get('new'), model };
controller.set("model", model); const steps = model.get('steps');
if (steps[0]) props['currentStep'] = steps[0];
controller.setProperties(props);
},
actions: {
refreshRoute() {
this.refresh();
}
} }
}); });

Datei anzeigen

@ -5,6 +5,12 @@ export default Discourse.Route.extend({
return CustomWizard.findAll(); return CustomWizard.findAll();
}, },
afterModel(model, transition) {
if (transition.intent.name !== 'adminWizard' && model.length > 0) {
this.transitionTo('adminWizard', model[0].id);
}
},
setupController(controller, model){ setupController(controller, model){
controller.set("model", model.toArray()); controller.set("model", model.toArray());
}, },

Datei anzeigen

@ -5,6 +5,12 @@ export default Discourse.Route.extend({
return CustomWizard.findAllSubmissions(); return CustomWizard.findAllSubmissions();
}, },
afterModel(model, transition) {
if (transition.intent.name !== 'adminWizardSubmissions' && model.length > 0) {
this.transitionTo('adminWizardSubmissions', model[0].id);
}
},
setupController(controller, model){ setupController(controller, model){
controller.set("model", model); controller.set("model", model);
} }

Datei anzeigen

@ -1,14 +1,16 @@
<table> <div class="wizard-submissions">
{{#each model.submissions as |s|}} <table>
<tr> {{#each model.submissions as |s|}}
{{#each-in s as |k v|}} <tr>
<th>{{k}}</th> {{#each-in s as |k v|}}
{{/each-in}} <th>{{k}}</th>
</tr> {{/each-in}}
<tr> </tr>
{{#each-in s as |k v|}} <tr>
<td>{{v}}</td> {{#each-in s as |k v|}}
{{/each-in}} <td>{{v}}</td>
</tr> {{/each-in}}
{{/each}} </tr>
</table> {{/each}}
</table>
</div>

Datei anzeigen

@ -1,22 +1,45 @@
<div class="form-horizontal"> <div class="admin-wizard settings">
<div>
<label for="name">{{i18n 'admin.wizard.name'}}</label> <div class="wizard-header">
{{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}} {{i18n 'admin.wizard.header'}}
</div> </div>
<div> <div class="setting">
{{input type='checkbox' checked=model.save_submissions}} <div class="setting-label">
<span for="save">{{i18n 'admin.wizard.save_submissions'}}</span> <h3>{{i18n 'admin.wizard.name'}}</h3>
</div>
<div class="setting-value">
{{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}}
</div>
</div> </div>
{{#if model.steps}} <div class="setting">
{{#each model.steps as |s|}} <div class="setting-label">
{{wizard-custom-step step=s fieldTypes=model.fieldTypes}} <h3>{{i18n 'admin.wizard.save_submissions'}}</h3>
{{d-button action='removeStep' actionParam=s label='admin.wizard.step.remove'}} </div>
<div class="setting-value">
{{input type='checkbox' checked=model.save_submissions}}
<span for="save">{{i18n 'admin.wizard.save_submissions_label'}}</span>
</div>
</div>
<div class="setting full">
<div class="setting-label">
<h3>{{i18n 'admin.wizard.url'}}</h3>
</div>
<a href="{{wizardUrl}}" target="_blank">{{wizardUrl}}</a>
</div>
<div class="wizard-links">
<div class="wizard-header">{{i18n 'admin.wizard.step.header'}}</div>
{{#each stepLinks as |s|}}
{{d-button action="changeStep" actionParam=s.id translatedLabel=s.title class=s.classes}}
{{d-button action="removeStep" actionParam=s.id icon='times' class='remove'}}
{{/each}} {{/each}}
{{/if}} {{d-button action='addStep' label='admin.wizard.add' icon='plus'}}
</div>
{{d-button action='addStep' label='admin.wizard.step.add'}} {{wizard-custom-step step=currentStep fieldTypes=model.fieldTypes}}
<div class='buttons'> <div class='buttons'>
<button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.wizard.save'}}</button> <button {{action "save"}} disabled={{disableSave}} class='btn btn-primary'>{{i18n 'admin.wizard.save'}}</button>

Datei anzeigen

@ -1,7 +1 @@
<div class="groups-type-index"> <div class="groups-type-index"></div>
<div>
{{#link-to 'adminWizard' 'new' class="btn"}}
{{d-icon "plus"}} {{i18n 'admin.wizard.new'}}
{{/link-to}}
</div>
</div>

Datei anzeigen

@ -7,9 +7,14 @@
</li> </li>
{{/each}} {{/each}}
</ul> </ul>
<div class="new-wizard">
{{#link-to 'adminWizard' 'new' class="btn"}}
{{d-icon "plus"}} {{i18n 'admin.wizard.new'}}
{{/link-to}}
</div>
</div> </div>
<div class="span13"> <div class="content">
{{outlet}} {{outlet}}
</div> </div>
</div> </div>

Datei anzeigen

@ -9,7 +9,7 @@
</ul> </ul>
</div> </div>
<div class="span13"> <div class="content">
{{outlet}} {{outlet}}
</div> </div>
</div> </div>

Datei anzeigen

@ -1,26 +1,96 @@
{{combo-box value=action.type content=types}} <div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.label"}}</h3>
</div>
<div class="setting-value">
{{input value=action.label}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.type"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.type content=types}}
</div>
</div>
{{#if createTopic}} {{#if createTopic}}
<label>{{i18n "admin.wizard.action.create_topic.category"}}</label> <div class="setting">
{{category-select-box value=action.category_id}} <div class="setting-label">
<label>{{i18n "admin.wizard.action.create_topic.title"}}</label> <h3>{{i18n "admin.wizard.action.create_topic.category"}}</h3>
{{combo-box value=action.title content=stepFields nameProperty="label"}} </div>
<label>{{i18n "admin.wizard.action.create_topic.post"}}</label> <div class="setting-value">
{{combo-box value=action.post content=stepFields nameProperty="label"}} {{category-select-box value=action.category_id}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.create_topic.title"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.title content=stepFields nameProperty="label"}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.create_topic.post"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.post content=stepFields nameProperty="label"}}
</div>
</div>
{{/if}} {{/if}}
{{#if sendMessage}} {{#if sendMessage}}
<label>{{i18n "admin.wizard.action.send_message.title"}}</label> <div class="setting">
{{combo-box value=action.title content=stepFields nameProperty="label"}} <div class="setting-label">
<label>{{i18n "admin.wizard.action.send_message.post"}}</label> <h3>{{i18n "admin.wizard.action.send_message.title"}}</h3>
{{combo-box value=action.post content=stepFields nameProperty="label"}} </div>
<label>{{i18n "admin.wizard.action.send_message.recipient"}}</label> <div class="setting-value">
{{user-selector single="true" {{combo-box value=action.title content=stepFields nameProperty="label"}}
includeMentionableGroups="true" </div>
usernames=action.username </div>
allowedUsers="true"}} <div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.send_message.post"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.post content=stepFields nameProperty="label"}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.send_message.recipient"}}</h3>
</div>
<div class="setting-value">
{{user-selector single="true"
includeMentionableGroups="true"
usernames=action.username
allowedUsers="true"}}
</div>
</div>
{{/if}} {{/if}}
{{#if updateProfile}} {{#if updateProfile}}
<label>{{i18n "admin.wizard.action.source"}}</label> <div class="setting">
{{combo-box value=action.source content=stepFields nameProperty="label"}} <div class="setting-label">
<label>{{i18n "admin.wizard.action.profile_field"}}</label> <h3>{{i18n "admin.wizard.action.source"}}</h3>
{{combo-box value=action.profile_field content=profileFields}} </div>
<div class="setting-value">
{{combo-box value=action.source content=stepFields nameProperty="label"}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n "admin.wizard.action.profile_field"}}</h3>
</div>
<div class="setting-value">
{{combo-box value=action.profile_field content=profileFields}}
</div>
</div>
{{/if}} {{/if}}

Datei anzeigen

@ -1 +0,0 @@
{{input type='text' value=choice.label}}

Datei anzeigen

@ -1,25 +1,51 @@
<div for={{field.id}}> <div class="setting">
<div> <div class="setting-label">
<span>{{i18n 'admin.wizard.field.label'}}</span> <h3>{{i18n 'admin.wizard.field.label'}}</h3>
{{text-field name="label" value=field.label}}
</div> </div>
<div> <div class="setting-value">
<span>{{i18n 'admin.wizard.field.description'}}</span> {{input name="label" value=field.label}}
{{text-field name="description" value=field.description}}
</div>
<div>
<span>{{i18n 'admin.wizard.field.type'}}</span>
{{combo-box value=field.type content=types}}
</div>
{{#if isDropdown}}
<span>{{i18n 'admin.wizard.field.choices_label'}}</span>
{{#each field.choices as |c|}}
{{wizard-custom-choice choice=c}}
{{/each}}
{{d-button action='addChoice' label='admin.wizard.field.add_choice'}}
{{/if}}
<div>
<span>{{i18n 'admin.wizard.field.required'}}</span>
{{input type='checkbox' checked=field.required}}
</div> </div>
</div> </div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n 'admin.wizard.field.description'}}</h3>
</div>
<div class="setting-value">
{{textarea name="description" value=field.description}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n 'admin.wizard.field.type'}}</h3>
</div>
<div class="setting-value">
{{combo-box value=field.type content=types}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<h3>{{i18n 'admin.wizard.field.required'}}</h3>
</div>
<div class="setting-value">
{{input type='checkbox' checked=field.required}}
<span>{{i18n 'admin.wizard.field.required_label'}}</span>
</div>
</div>
{{#if isDropdown}}
<div class="wizard-dropdown-choices">
<div class="wizard-header small">
{{i18n 'admin.wizard.field.choices_label'}}
</div>
{{#each dropdownChoices as |c|}}
<span class='wizard-dropdown-choice'>
{{input type='text' value=c.label}}
</span>
{{d-button action='removeChoice' actionParam=c icon='times'}}
{{/each}}
{{d-button action='addChoice' label='admin.wizard.add' icon='plus'}}
</div>
{{/if}}

Datei anzeigen

@ -1,30 +1,52 @@
<div> <div class="setting">
<label for="title">{{i18n 'admin.wizard.step.title'}}</label> <div class="setting-label">
{{text-field name="title" value=step.title placeholderKey="admin.wizard.step.title_placeholder"}} <h3>{{i18n 'admin.wizard.step.title'}}</h3>
</div>
<div class="setting-value">
{{input name="title" value=step.title placeholderKey="admin.wizard.step.title_placeholder"}}
</div>
</div> </div>
<div> <div class="setting">
<label for="banner">{{i18n 'admin.wizard.step.banner'}}</label> <div class="setting-label">
{{input name="banner" value=step.banner placeholderKey="admin.wizard.step.banner_placeholder"}} <h3>{{i18n 'admin.wizard.step.banner'}}</h3>
</div>
<div class="setting-value">
{{input name="banner" value=step.banner placeholderKey="admin.wizard.step.banner_placeholder"}}
</div>
</div> </div>
<div> <div class="setting">
<label for="description">{{i18n 'admin.wizard.step.description'}}</label> <div class="setting-label">
{{input name="description" value=step.description placeholderKey="admin.wizard.step.description_placeholder"}} <h3>{{i18n 'admin.wizard.step.description'}}</h3>
</div>
<div class="setting-value">
{{textarea name="description" value=step.description placeholderKey="admin.wizard.step.description_placeholder"}}
</div>
</div> </div>
{{#each step.fields as |f|}} <div class="wizard-links">
{{wizard-custom-field field=f types=fieldTypes}} <div class="wizard-header medium">{{i18n 'admin.wizard.field.header'}}</div>
{{d-button action='removeField' actionParam=f label="admin.wizard.field.remove"}} {{#each fieldLinks as |f|}}
{{/each}} {{d-button action="changeField" actionParam=f.id translatedLabel=f.label class=f.classes}}
{{d-button action='removeField' actionParam=f.id icon='times' class='remove'}}
{{/each}}
{{d-button action='addField' label='admin.wizard.add' icon='plus'}}
</div>
{{d-button action='addField' label='admin.wizard.field.add'}} {{#if currentField}}
{{wizard-custom-field field=currentField types=fieldTypes removeField="removeField"}}
{{#each step.actions as |a|}} {{/if}}
{{wizard-custom-action action=a stepFields=step.fields}}
{{d-button action='removeAction' actionParam=a label="admin.wizard.action.remove"}} <div class="wizard-links">
{{/each}} <div class="wizard-header medium">{{i18n 'admin.wizard.action.header'}}</div>
{{#each actionLinks as |a|}}
{{#if allowAddAction}} {{d-button action="changeAction" actionParam=a.id translatedLabel=a.label class=a.classes}}
{{d-button action='addAction' label='admin.wizard.action.add'}} {{d-button action='removeAction' actionParam=a.id icon='times' class='remove'}}
{{/each}}
{{d-button action='addAction' label='admin.wizard.add' icon='plus'}}
</div>
{{#if currentAction}}
{{wizard-custom-action action=currentAction stepFields=step.fields removeAction="removeAction"}}
{{/if}} {{/if}}

Datei anzeigen

@ -65,6 +65,17 @@ export default {
}); });
WizardStep.reopen({ WizardStep.reopen({
bannerImage: function() {
const src = this.get('step.banner');
if (!src) return;
if (src.indexOf('/uploads/') > -1) {
return getUrl(src);
} else {
return getUrl(`/images/wizard/${src}`);
};
}.property('step.banner'),
advance() { advance() {
this.set('saving', true); this.set('saving', true);
this.get('step').save() this.get('step').save()

Datei anzeigen

@ -1,3 +1,78 @@
.wizards-nav-button { .wizards-nav-button {
@extend .nav-pills; @extend .nav-pills;
float: left;
}
.new-wizard {
margin-top: 15px;
}
.wizard-header {
font-size: 1.3em;
margin-bottom: 15px;
&.medium {
font-size: 1.1em;
}
&.small {
font-size: 0.97em;
}
}
.content-list + .content {
overflow: hidden;
}
.admin-wizard.settings {
margin-left: 30px;
margin-right: 30px;
.setting {
display: inline-block;
vertical-align: top;
min-width: 49%;
.setting-label {
width: 20%;
}
&.full {
width: 100%;
.setting-label {
width: 75px;
}
}
}
}
.wizard-links {
margin-bottom: 20px;
.remove {
margin-right: 10px;
}
}
.wizard-custom-step {
display: inline-block;
width: 100%;
margin-bottom: 20px;
padding: 15px;
background-color: dark-light-diff($primary, $secondary, 96%, -65%);
}
.wizard-dropdown-choices {
margin-bottom: 25px;
}
.wizard-dropdown-choice {
display: inline-block;
}
.wizard-submissions {
padding: 0 20px;
display: inline-block;
overflow: scroll;
} }

Datei anzeigen

@ -8,41 +8,44 @@ en:
submissions_label: "Submissions" submissions_label: "Submissions"
name: "Name" name: "Name"
name_placeholder: "name of the wizard" name_placeholder: "name of the wizard"
save_submissions: "Save wizard submissions" save_submissions: "Save"
save: "Save Wizard" save_submissions_label: "Save wizard submissions"
save: "Save Changes"
remove: "Delete Wizard" remove: "Delete Wizard"
header: "Wizard"
add: "Add"
url: "Url"
step: step:
title: "Step Title" header: "Steps"
title: "Title"
title_placeholder: "This will appear at the top of the step" title_placeholder: "This will appear at the top of the step"
banner: "Step Banner" banner: "Banner"
banner_placeholder: "This image will appear under the title" banner_placeholder: "Image url"
description: "Step Description" description: "Description"
description_placeholder: "This will appear underneath the title and / or title" description_placeholder: "This will appear underneath the title and / or title"
add: "Add Step"
remove: "Remove Step"
field: field:
add: "Add Field" header: "Fields"
remove: "Remove Field" label: "Label"
label: "Field Name" description: "Description"
description: "Field Description" type: "Type"
type: "Field Type"
choices_label: "Dropdown Choices" choices_label: "Dropdown Choices"
add_choice: "Add Choice" add_choice: "Add"
required: "Field Required" required: "Required"
required_label: "Field is Required"
action: action:
add: "Add Action" header: "Actions"
remove: "Remove Action" label: "Label"
source: "Source" type: "Type"
send_message: send_message:
label: "Send Message" label: "Send Message"
title: "Message Title" title: "Title"
post: "Message Post" post: "Post"
recipient: "Message Recipient" recipient: "Recipient"
create_topic: create_topic:
label: "Create Topic" label: "Create Topic"
title: "Topic Title" title: "Title"
post: "Topic Post" post: "Post"
category: "Topic Category" category: "Category"
update_profile: update_profile:
label: "Update Profile" label: "Update Profile"
field: "Profile Field" field: "Profile Field"