0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-10 04:12:53 +01:00
Dieser Commit ist enthalten in:
Angus McLeod 2020-04-06 11:54:16 +10:00
Ursprung 7b3ed54f29
Commit 04d7fc1c59
27 geänderte Dateien mit 283 neuen und 161 gelöschten Zeilen

Datei anzeigen

@ -19,8 +19,10 @@ export default Component.extend({
@on('didInsertElement')
@observes('action.type')
updateId() {
if (this.action.type) this.set('action.id', generateName(this.action.type));
setLabel() {
if (this.action.type) {
this.set('action.label', generateName(this.action.type));
};
},
@discourseComputed('action.type')

Datei anzeigen

@ -34,14 +34,18 @@ export default Component.extend({
let options = {
wizardFieldSelection: true,
textSelection: 'key,value',
userFieldSelection: 'key,value'
userFieldSelection: 'key,value',
context: 'field'
}
if (this.isDropdown) {
options.wizardFieldSelection = 'key,value';
options.listSelection = 'assignment';
options.inputTypes = 'pair,assignment';
options.pairConnector = 'equal';
options.pairConnector = 'association';
options.keyPlaceholder = 'admin.wizard.key';
options.valuePlaceholder = 'admin.wizard.value';
options.outputDefaultSelection = 'list';
}
return options;
@ -52,23 +56,29 @@ export default Component.extend({
let options = {
wizardFieldSelection: true,
textSelection: 'key,value',
userFieldSelection: 'key,value'
userFieldSelection: 'key,value',
context: 'field'
}
if (!this.isDropdown) {
let selectionType = {
category: 'category',
tag: 'tag',
group: 'group',
dropdown: 'text'
}[fieldType];
options[`${selectionType}Selection`] = 'output';
options.outputDefaultSelection = selectionType;
}
let outputSelectionType = {
category: 'category',
tag: 'tag',
group: 'group',
dropdown: 'text'
}[fieldType];
options[`${outputSelectionType}Selection`] = 'output';
options.outputDefaultSelection = outputSelectionType;
return options;
},
@observes('field.type')
clearInputs() {
this.set('field.content', null);
this.set('field.prefill', null);
},
actions: {
imageUploadDone(upload) {
this.set("field.image", upload.url);

Datei anzeigen

@ -5,22 +5,8 @@ import Component from "@ember/component";
export default Component.extend({
classNames: 'wizard-custom-step',
currentField: null,
currentAction: null,
disableId: not('step.isNew'),
@on('didInsertElement')
@observes('step')
resetCurrentObjects() {
const fields = this.step.fields;
const actions = this.step.actions;
this.setProperties({
currentField: fields.length ? fields[0] : null,
currentAction: actions.length ? actions[0] : null
});
},
@discourseComputed('wizardFields', 'wizard.steps')
requiredContent(wizardFields, steps) {
let content = wizardFields;

Datei anzeigen

@ -35,15 +35,15 @@ export default Component.extend({
@discourseComputed('type')
header: (type) => `admin.wizard.${type}.header`,
@discourseComputed('items.@each.id', 'current')
links(items, current) {
@discourseComputed('current', 'items.@each.id', 'items.@each.label')
links(current, items) {
if (!items) return;
return items.map((item) => {
if (item) {
const id = item.id;
const type = this.type;
const label = type === 'action' ? id : (item.label || item.title || id);
const label = item.label || item.title || id;
let link = { id, label };
let classes = 'btn';

Datei anzeigen

@ -2,6 +2,7 @@ import { computed, set } from "@ember/object";
import { alias, equal, or } from "@ember/object/computed";
import { newPair, connectorContent, inputTypesContent } from '../lib/wizard-mapper';
import Component from "@ember/component";
import { observes } from "discourse-common/utils/decorators";
export default Component.extend({
classNameBindings: [':mapper-input', 'type'],

Datei anzeigen

@ -1,19 +1,14 @@
import discourseComputed from 'discourse-common/utils/decorators';
import { snakeCase } from '../lib/wizard';
import { selectionTypes } from '../lib/wizard-mapper';
import Component from "@ember/component";
export default Component.extend({
tagName: 'a',
classNameBindings: ['type', 'active'],
classNameBindings: ['active'],
@discourseComputed('type', 'activeType')
@discourseComputed('item.type', 'activeType')
active(type, activeType) { return type === activeType },
@discourseComputed('type')
label(type) { return I18n.t(`admin.wizard.selector.label.${snakeCase(type)}`) },
click() {
this.toggle(this.type)
this.toggle(this.item.type)
}
})

Datei anzeigen

@ -1,10 +1,11 @@
import { alias, or } from "@ember/object/computed";
import { alias, or, gt } from "@ember/object/computed";
import { computed } from "@ember/object";
import { default as discourseComputed, observes } from "discourse-common/utils/decorators";
import { getOwner } from 'discourse-common/lib/get-owner';
import { defaultSelectionType, selectionTypes } from '../lib/wizard-mapper';
import { snakeCase, selectKitContent } from '../lib/wizard';
import Component from "@ember/component";
import { bind } from "@ember/runloop";
export default Component.extend({
classNames: 'mapper-selector',
@ -28,25 +29,50 @@ export default Component.extend({
groupEnabled: computed('options.groupSelection', 'inputType', function() { return this.optionEnabled('groupSelection') }),
userEnabled: computed('options.userSelection', 'inputType', function() { return this.optionEnabled('userSelection') }),
listEnabled: computed('options.listSelection', 'inputType', function() { return this.optionEnabled('listSelection') }),
hasTypes: gt('selectorTypes.length', 1),
showTypes: false,
didInsertElement() {
$(document).on("click", bind(this, this.documentClick));
},
willDestroyElement() {
$(document).off("click", bind(this, this.documentClick));
},
documentClick(e) {
let $element = $(this.element);
let $target = $(e.target);
if (!$target.hasClass('type-selector-icon') &&
$target.closest($element).length < 1 &&
this._state !== "destroying") {
this.set("showTypes", false);
}
},
@discourseComputed
selectorTypes() {
return selectionTypes.filter(type => (this[`${type}Enabled`]))
.map(type => ({ type, label: this.typeLabel(type) }));
},
@discourseComputed('activeType')
selectorTypes(activeType) {
return selectionTypes.filter(type => (this[`${type}Enabled`]));
activeTypeLabel(activeType) {
return this.typeLabel(activeType);
},
@discourseComputed
userFields() {
const controller = getOwner(this).lookup('controller:admin-wizard');
return controller.model.userFields;
typeLabel(type) {
return I18n.t(`admin.wizard.selector.label.${snakeCase(type)}`)
},
@discourseComputed
wizardFields() {
const controller = getOwner(this).lookup('controller:admin-wizard');
return controller.wizardFields;
@discourseComputed('showTypes')
typeSelectorIcon(showTypes) {
return showTypes ? 'chevron-down' : 'chevron-right';
},
@observes('options.@each')
@observes('options.@each', 'inputType')
resetActiveType() {
this.set('activeType', defaultSelectionType(this.selectorType, this.options));
},
@ -58,7 +84,15 @@ export default Component.extend({
@discourseComputed('activeType')
comboBoxContent(activeType) {
return this[`${activeType}Fields`];
const controller = getOwner(this).lookup('controller:admin-wizard');
let content = controller[`${activeType}s`];
if (activeType === 'wizardField' && this.options.context === 'field') {
const currentField = controller.currentField;
content = content.filter(field => field.id !== currentField.id);
}
return content;
},
@discourseComputed('activeType')
@ -70,18 +104,19 @@ export default Component.extend({
}[activeType];
},
@discourseComputed('activeType')
placeholder(activeType) {
@discourseComputed('activeType', 'inputType')
placeholderKey(activeType, inputType) {
if (activeType === 'text' && this.options[`${this.selectorType}Placeholder`]) {
return this.options[`${this.selectorType}Placeholder`];
} else {
return `admin.wizard.selector.placeholder.${snakeCase(activeType)}`;
}
return `admin.wizard.selector.placeholder.${snakeCase(activeType)}`;
},
@discourseComputed('activeType')
multiSelectOptions(activeType) {
let result = {
none: this.placeholder
none: this.placeholderKey
};
if (activeType === 'list') {
@ -111,6 +146,11 @@ export default Component.extend({
actions: {
toggleType(type) {
this.set('activeType', type);
this.set('showTypes', false);
},
toggleTypes() {
this.toggleProperty('showTypes')
}
}
})

Datei anzeigen

@ -18,11 +18,13 @@ export default Component.extend({
let result = {
inputTypes: options.inputTypes || 'conditional,assignment',
pairConnector: options.pairConnector || null,
outputConnector: options.outputConnector || null
outputConnector: options.outputConnector || null,
context: options.context || null
}
let inputTypes = ['key', 'value', 'output'];
inputTypes.forEach(type => {
result[`${type}Placeholder`] = options[`${type}Placeholder`] || null;
result[`${type}DefaultSelection`] = options[`${type}DefaultSelection`] || null;
});
@ -37,14 +39,12 @@ export default Component.extend({
return result;
},
@observes('options.inputTypes')
clearInputs() {
this.get('inputs').clear();
},
actions: {
add() {
if (!this.get('inputs')) this.set('inputs', A());
if (!this.get('inputs')) {
this.set('inputs', A());
}
this.get('inputs').pushObject(newInput(this.inputOptions));
},

Datei anzeigen

@ -1,5 +1,5 @@
import { default as discourseComputed, observes } from 'discourse-common/utils/decorators';
import { notEmpty } from "@ember/object/computed";
import { default as discourseComputed, observes, on } from 'discourse-common/utils/decorators';
import { notEmpty, alias } from "@ember/object/computed";
import showModal from 'discourse/lib/show-modal';
import { generateId } from '../lib/wizard';
import { buildProperties } from '../lib/wizard-json';
@ -10,15 +10,27 @@ import Controller from "@ember/controller";
export default Controller.extend({
hasName: notEmpty('model.name'),
userFields: alias('model.userFields'),
@observes('currentStep')
resetCurrentObjects() {
const currentStep = this.currentStep;
const fields = currentStep.fields;
const actions = currentStep.actions;
this.setProperties({
currentField: fields.length ? fields[0] : null,
currentAction: actions.length ? actions[0] : null
});
init() {
this._super();
scheduleOnce('afterRender', () => ($("body").addClass('admin-wizard')));
},
@observes('model.name')
setId() {
if (!this.model.existingId) this.set('model.id', generateId(this.model.name));
if (!this.model.existingId) {
this.set('model.id', generateId(this.model.name));
}
},
@discourseComputed('model.id')

Datei anzeigen

@ -1,4 +1,4 @@
import { default as computed } from 'discourse-common/utils/decorators';
import { default as discourseComputed } from 'discourse-common/utils/decorators';
import { scheduleOnce } from "@ember/runloop";
import Controller from "@ember/controller";
@ -24,12 +24,12 @@ export default Controller.extend({
});
},
@computed('date', 'time')
@discourseComputed('date', 'time')
dateTime: function(date, time) {
return moment(date + 'T' + time).format();
},
@computed('dateTime')
@discourseComputed('dateTime')
submitDisabled(dateTime) {
return moment().isAfter(dateTime);
},

Datei anzeigen

@ -67,13 +67,13 @@ function connectorContent(connectorType, inputType, opts) {
const selectionTypes = [
'text',
'list',
'wizardField',
'userField',
'group',
'category',
'tag',
'user',
'list'
'user'
]
function defaultSelectionType(inputType, options = {}) {
@ -117,8 +117,6 @@ function newPair(inputType, options = {}) {
function newInput(options = {}) {
const inputType = defaultInputType(options);
console.log(inputType);
let params = {
type: inputType,
pairs: A(
@ -134,7 +132,10 @@ function newInput(options = {}) {
)
}
if (['conditional', 'assignment'].indexOf(inputType) > -1) {
if (['conditional', 'assignment'].indexOf(inputType) > -1 ||
options.outputDefaultSelection ||
options.outputConnector) {
params['output_type'] = defaultSelectionType('output', options);
params['connector'] = defaultConnector('output', inputType, options);
}

Datei anzeigen

@ -6,7 +6,7 @@ function generateName(id) {
return id ? sentenceCase(id) : '';
}
function generateId(name) {
function generateId(name, opts={}) {
return name ? snakeCase(name) : '';
}
@ -167,7 +167,9 @@ const actionTypes = [
'add_to_group',
'route_to',
'open_composer'
];
].filter(function(type) {
return Discourse.SiteSettings.wizard_api_features || type !== 'send_to_api';
});
export {
selectKitContent,

Datei anzeigen

@ -1,10 +1,10 @@
import { ajax } from 'discourse/lib/ajax';
import { default as computed } from 'discourse-common/utils/decorators';
import { default as discourseComputed } from 'discourse-common/utils/decorators';
import EmberObject from "@ember/object";
import { A } from "@ember/array";
const CustomWizardApi = EmberObject.extend({
@computed('name')
@discourseComputed('name')
redirectUri(name) {
let nameParam = name.toString().dasherize();
const baseUrl = location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: '');

Datei anzeigen

@ -48,7 +48,6 @@
</div>
<div class="wizard-settings">
<div class="setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.required'}}</label>
@ -113,6 +112,7 @@
inputs=model.permitted
options=(hash
singular=true
context='wizard'
inputTypes='assignment'
groupSelection='output'
textSelection='key,value'
@ -153,7 +153,12 @@
{{wizard-links type="step" current=currentStep items=model.steps}}
{{#if currentStep}}
{{wizard-custom-step step=currentStep wizard=model wizardFields=wizardFields}}
{{wizard-custom-step
step=currentStep
wizard=model
currentField=currentField
currentAction=currentAction
wizardFields=wizardFields}}
{{/if}}
<div class='admin-wizard-buttons'>

Datei anzeigen

@ -1,7 +1,9 @@
{{#admin-nav}}
{{nav-item route='adminWizardsCustom' label='admin.wizard.custom_label'}}
{{nav-item route='adminWizardsSubmissions' label='admin.wizard.submissions_label'}}
{{nav-item route='adminWizardsApis' label='admin.wizard.api.nav_label'}}
{{#if siteSettings.wizard_api_features}}
{{nav-item route='adminWizardsApis' label='admin.wizard.api.nav_label'}}
{{/if}}
{{nav-item route='adminWizardsTransfer' label='admin.wizard.transfer.nav_label'}}
{{/admin-nav}}

Datei anzeigen

@ -26,6 +26,7 @@
options=(hash
wizardFieldSelection=true
userFieldSelection='key,value'
context='action'
)}}
</div>
</div>
@ -83,6 +84,7 @@
userFieldSelection='key,value'
categorySelection='output'
outputDefaultSelection='category'
context='action'
)}}
</div>
</div>
@ -99,6 +101,7 @@
tagSelection='output'
wizardFieldSelection=true
userFieldSelection='key,value'
context='action'
)}}
</div>
</div>
@ -120,6 +123,7 @@
groupSelection='key,value'
userSelection='output'
outputDefaultSelection='user'
context='action'
)}}
</div>
</div>
@ -139,6 +143,7 @@
wizardFieldSelection='value'
keyDefaultSelection='userField'
keyPlaceholder='admin.wizard.action.update_profile.key'
context='action'
)}}
</div>
{{/if}}
@ -209,6 +214,7 @@
userFieldSelection='key,value,assignment'
groupSelection='value,output'
outputDefaultSelection='group'
context='action'
)}}
</div>
</div>
@ -246,6 +252,7 @@
wizardFieldSelection='value'
userFieldSelection='value'
keyPlaceholder='admin.wizard.action.custom_fields.key'
context='action'
)}}
</div>
</div>
@ -265,6 +272,7 @@
wizardFieldSelection=true
userFieldSelection=true
groupSelection=true
context='action'
)}}
</div>
</div>

Datei anzeigen

@ -123,7 +123,9 @@
</div>
<div class="setting-value">
{{wizard-mapper inputs=field.prefill options=prefillOptions}}
{{wizard-mapper
inputs=field.prefill
options=prefillOptions}}
</div>
</div>
{{/if}}
@ -135,7 +137,9 @@
</div>
<div class="setting-value">
{{wizard-mapper inputs=field.content options=contentOptions}}
{{wizard-mapper
inputs=field.content
options=contentOptions}}
</div>
</div>
{{/if}}

Datei anzeigen

@ -34,62 +34,66 @@
</div>
</div>
{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
{{#if siteSettings.wizard_step_advanced}}
{{wizard-advanced-toggle showAdvanced=step.showAdvanced}}
{{#if step.showAdvanced}}
<div class="advanced-settings">
{{#if step.showAdvanced}}
<div class="advanced-settings">
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.step.required_data.label'}}</label>
</div>
<div class="setting-value">
{{wizard-mapper
inputs=step.required_data
options=(hash
wizardFieldSelection='value'
userFieldSelection='value'
keyPlaceholder="admin.wizard.submission_key"
)}}
{{#if step.required_data}}
<div class="required-data-message">
<div class="label">
{{i18n 'admin.wizard.step.required_data.not_permitted_message'}}
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.step.required_data.label'}}</label>
</div>
<div class="setting-value">
{{wizard-mapper
inputs=step.required_data
options=(hash
wizardFieldSelection='value'
userFieldSelection='value'
keyPlaceholder="admin.wizard.submission_key"
context='step'
)}}
{{#if step.required_data}}
<div class="required-data-message">
<div class="label">
{{i18n 'admin.wizard.step.required_data.not_permitted_message'}}
</div>
{{input value=step.required_data_message}}
</div>
{{input value=step.required_data_message}}
</div>
{{/if}}
{{/if}}
</div>
</div>
</div>
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.step.permitted_params.label'}}</label>
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.step.permitted_params.label'}}</label>
</div>
<div class="setting-value">
{{wizard-mapper
inputs=step.permitted_params
options=(hash
pairConnector='set'
keyPlaceholder='admin.wizard.param_key'
valuePlaceholder='admin.wizard.submission_key'
context='step'
)}}
</div>
</div>
<div class="setting-value">
{{wizard-mapper
inputs=step.permitted_params
options=(hash
pairConnector='set'
keyPlaceholder='admin.wizard.param_key'
valuePlaceholder='admin.wizard.submission_key'
)}}
</div>
</div>
<div class="setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.translation'}}</label>
<div class="setting">
<div class="setting-label">
<label>{{i18n 'admin.wizard.translation'}}</label>
</div>
<div class="setting-value">
{{input
name="key"
value=step.key
placeholderKey="admin.wizard.translation_placeholder"}}
</div>
</div>
<div class="setting-value">
{{input
name="key"
value=step.key
placeholderKey="admin.wizard.translation_placeholder"}}
</div>
</div>
</div>
</div>
{{/if}}
{{/if}}
{{wizard-links type="field" current=currentField items=step.fields}}

Datei anzeigen

@ -32,7 +32,7 @@
<div class="output mapper-block">
{{wizard-mapper-selector
selectorType='output'
inputType=inputType
inputType=input.type
value=input.output
activeType=input.output_type
options=options}}

Datei anzeigen

@ -1,10 +1,23 @@
<div class="type-selector">
{{#each selectorTypes as |type|}}
{{wizard-mapper-selector-type
activeType=activeType
type=type
toggle=(action 'toggleType')}}
{{/each}}
{{#if hasTypes}}
<a {{action "toggleTypes"}} class="active">
{{activeTypeLabel}}
{{d-icon typeSelectorIcon class="type-selector-icon"}}
</a>
{{#if showTypes}}
<div class="selector-types">
{{#each selectorTypes as |item|}}
{{wizard-mapper-selector-type
activeType=activeType
item=item
toggle=(action 'toggleType')}}
{{/each}}
</div>
{{/if}}
{{else}}
<span>{{activeTypeLabel}}</span>
{{/if}}
</div>
<div class="input">
@ -12,7 +25,7 @@
{{input
type="text"
value=value
placeholder=(i18n placeholder)}}
placeholder=(i18n placeholderKey)}}
{{/if}}
{{#if showComboBox}}
@ -21,7 +34,7 @@
content=comboBoxContent
onChange=(action (mut value))
options=(hash
none=placeholder
none=placeholderKey
)}}
{{/if}}
@ -34,7 +47,9 @@
{{/if}}
{{#if showList}}
{{value-list values=value}}
{{value-list
values=value
addKey=placeholderKey}}
{{/if}}
{{#if showTag}}
@ -42,14 +57,14 @@
tags=value
filterable=true
options=(hash
none=placeholder
none=placeholderKey
)}}
{{/if}}
{{#if showUser}}
{{user-selector
includeMessageableGroups='true'
placeholderKey=placeholder
placeholderKey=placeholderKey
usernames=value
autocomplete="discourse"}}
{{/if}}

Datei anzeigen

@ -51,12 +51,20 @@ body.admin-wizard {
padding: 20px;
}
.wizard-links {
&.action, &.field {
margin-top: 50px;
}
// style workdarounds for wizard_step_advanced site setting - to be refactored
.wizard-custom-step .wizard-advanced-toggle + .wizard-links.field,
.wizard-custom-step .advanced-settings + .wizard-links.field,
.wizard-links.action {
margin-top: 40px;
}
.wizard-links.field {
margin-top: 20px;
}
// end workdarounds //
.wizard-settings > .advanced-settings > div.setting {
margin-bottom: 0;
}

Datei anzeigen

@ -95,6 +95,8 @@
.type-selector a {
color: $primary;
margin-right: 4px;
display: flex;
align-items: center;
&.active {
color: $tertiary;
@ -104,6 +106,22 @@
&:last-of-type {
margin-right: 0;
}
.type-selector-icon {
margin-left: 2px;
font-size: 0.7em;
}
}
.selector-types {
box-shadow: shadow('dropdown');
position: absolute;
background: $secondary;
z-index: 200;
padding: 5px 7px;
display: flex;
flex-direction: column;
border: 1px solid $primary-low;
}
.value-list .remove-value-btn {

Datei anzeigen

@ -73,8 +73,8 @@ en:
selector:
label:
text: "text"
wizard_field: "wizard "
user_field: "user "
wizard_field: "wizard field"
user_field: "user field"
user: "user"
category: "category"
tag: "tag"
@ -141,6 +141,7 @@ en:
less: '<'
greater_or_equal: '>='
less_or_equal: '<='
association: '→'
action:
header: "Actions"

Datei anzeigen

@ -26,3 +26,5 @@ en:
custom_wizard_enabled: "Enable custom wizards."
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects."
wizard_recognised_image_upload_formats: "File types which will result in upload displaying an image preview"
wizard_step_advanced: "Enable advanced settings for wizard steps (experimental)."
wizard_api_features: "Enable API features (experimental)."

Datei anzeigen

@ -15,3 +15,9 @@ plugins:
refresh: true
type: list
list_type: compact
wizard_step_advanced:
client: true
default: false
wizard_api_features:
client: true
default: false