DEV: Define form template field inputs (#20430)

This commit is contained in:
Keegan George
2023-03-01 11:07:13 -08:00
committed by GitHub
parent 8b67a534a0
commit 666b4a7e6b
37 changed files with 963 additions and 130 deletions

View File

@@ -1,4 +1,4 @@
<div class="form-templates--form">
<div class="form-templates__form">
<div class="control-group">
<label for="template-name">
{{i18n "admin.form_templates.new_template_form.name.label"}}
@@ -6,24 +6,38 @@
<TextField
@value={{this.templateName}}
@name="template-name"
@class="form-templates--form-name-input"
@class="form-templates__form-name-input"
@placeholderKey="admin.form_templates.new_template_form.name.placeholder"
/>
</div>
<div class="control-group form-templates--quick-insert-field-buttons">
<span>
{{I18n "admin.form_templates.quick_insert_fields.add_new_field"}}
</span>
{{#each this.quickInsertFields as |field|}}
<div class="control-group form-templates__editor">
<div class="form-templates__quick-insert-field-buttons">
<span>
{{I18n "admin.form_templates.quick_insert_fields.add_new_field"}}
</span>
{{#each this.quickInsertFields as |field|}}
<DButton
@class="btn-flat btn-icon-text quick-insert-{{field.type}}"
@icon={{field.icon}}
@label="admin.form_templates.quick_insert_fields.{{field.type}}"
@action={{this.onInsertField}}
@actionParam={{field.type}}
/>
{{/each}}
<DButton
@class="btn-flat btn-icon-text quick-insert-{{field.type}}"
@icon={{field.icon}}
@label="admin.form_templates.quick_insert_fields.{{field.type}}"
@action={{this.onInsertField}}
@actionParam={{field.type}}
class="btn-flat btn-icon-text form-templates__validations-modal-button"
@label="admin.form_templates.validations_modal.button_title"
@icon="check-circle"
@action={{this.showValidationOptionsModal}}
/>
{{/each}}
</div>
<DButton
@class="form-templates__preview-button"
@icon="eye"
@label="admin.form_templates.new_template_form.preview"
@action={{this.showPreview}}
@disabled={{this.disablePreviewButton}}
/>
</div>
<div class="control-group">
@@ -36,7 +50,7 @@
@label="admin.form_templates.new_template_form.submit"
@icon="check"
@action={{this.onSubmit}}
@disabled={{this.formSubmitted}}
@disabled={{this.disableSubmitButton}}
/>
<DButton

View File

@@ -6,14 +6,15 @@ import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { templateFormFields } from "admin/lib/template-form-fields";
import FormTemplate from "admin/models/form-template";
import showModal from "discourse/lib/show-modal";
export default class FormTemplateForm extends Component {
@service router;
@service dialog;
@tracked formSubmitted = false;
@tracked templateContent = this.args.model?.template || "";
@tracked templateName = this.args.model?.name || "";
isEditing = this.args.model?.id ? true : false;
templateName = this.args.model?.name;
quickInsertFields = [
{
type: "checkbox",
@@ -41,6 +42,17 @@ export default class FormTemplateForm extends Component {
},
];
get disablePreviewButton() {
return Boolean(!this.templateName.length || !this.templateContent.length);
}
get disableSubmitButton() {
return (
Boolean(!this.templateName.length || !this.templateContent.length) ||
this.formSubmitted
);
}
@action
onSubmit() {
if (!this.formSubmitted) {
@@ -54,27 +66,17 @@ export default class FormTemplateForm extends Component {
if (this.isEditing) {
postData["id"] = this.args.model.id;
FormTemplate.updateTemplate(this.args.model.id, postData)
.then(() => {
this.formSubmitted = false;
this.router.transitionTo("adminCustomizeFormTemplates.index");
})
.catch((e) => {
popupAjaxError(e);
this.formSubmitted = false;
});
} else {
FormTemplate.createTemplate(postData)
.then(() => {
this.formSubmitted = false;
this.router.transitionTo("adminCustomizeFormTemplates.index");
})
.catch((e) => {
popupAjaxError(e);
this.formSubmitted = false;
});
}
FormTemplate.createOrUpdateTemplate(postData)
.then(() => {
this.formSubmitted = false;
this.router.transitionTo("adminCustomizeFormTemplates.index");
})
.catch((e) => {
popupAjaxError(e);
this.formSubmitted = false;
});
}
@action
@@ -106,4 +108,33 @@ export default class FormTemplateForm extends Component {
this.templateContent += `\n${structure}`;
}
}
@action
showValidationOptionsModal() {
return showModal("admin-form-template-validation-options", {
admin: true,
});
}
@action
showPreview() {
const data = {
name: this.templateName,
template: this.templateContent,
};
if (this.isEditing) {
data["id"] = this.args.model.id;
}
FormTemplate.validateTemplate(data)
.then(() => {
return showModal("form-template-form-preview", {
model: {
content: this.templateContent,
},
});
})
.catch(popupAjaxError);
}
}

View File

@@ -12,8 +12,7 @@ export default class FormTemplateRowItem extends Component {
@action
viewTemplate() {
showModal("admin-customize-form-template-view", {
admin: true,
showModal("customize-form-template-view", {
model: this.args.template,
refreshModel: this.args.refreshModel,
});

View File

@@ -1,37 +0,0 @@
import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import I18n from "I18n";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { ajax } from "discourse/lib/ajax";
export default class AdminCustomizeFormTemplateView extends Controller.extend(
ModalFunctionality
) {
@service router;
@service dialog;
@action
editTemplate() {
this.router.transitionTo("adminCustomizeFormTemplates.edit", this.model);
}
@action
deleteTemplate() {
return this.dialog.yesNoConfirm({
message: I18n.t("admin.form_templates.delete_confirm", {
template_name: this.model.name,
}),
didConfirm: () => {
ajax(`/admin/customize/form-templates/${this.model.id}.json`, {
type: "DELETE",
})
.then(() => {
this.refreshModel();
})
.catch(popupAjaxError);
},
});
}
}

View File

@@ -0,0 +1,35 @@
import Controller from "@ember/controller";
import ModalFunctionality from "discourse/mixins/modal-functionality";
import I18n from "I18n";
export default class AdminFormTemplateValidationOptions extends Controller.extend(
ModalFunctionality
) {
TABLE_HEADER_KEYS = ["key", "type", "description"];
VALIDATION_KEYS = ["required", "minimum", "maximum", "pattern"];
get tableHeaders() {
const translatedHeaders = [];
this.TABLE_HEADER_KEYS.forEach((header) => {
translatedHeaders.push(
I18n.t(`admin.form_templates.validations_modal.table_headers.${header}`)
);
});
return translatedHeaders;
}
get validations() {
const translatedValidations = [];
const prefix = "admin.form_templates.validations_modal.validations";
this.VALIDATION_KEYS.forEach((validation) => {
translatedValidations.push({
key: I18n.t(`${prefix}.${validation}.key`),
type: I18n.t(`${prefix}.${validation}.type`),
description: I18n.t(`${prefix}.${validation}.description`),
});
});
return translatedValidations;
}
}

View File

@@ -1,70 +1,75 @@
// TODO(@keegan): Add translations for template strings
import I18n from "I18n";
export const templateFormFields = [
{
type: "checkbox",
structure: `- type: checkbox
choices:
- "Option 1"
- "Option 2"
- "Option 3"
attributes:
label: "Enter question here"
description: "Enter description here"
validations:
required: true`,
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
validations:
# ${I18n.t("admin.form_templates.field_placeholders.validations")}`,
},
{
type: "input",
structure: `- type: input
attributes:
label: "Enter input label here"
description: "Enter input description here"
placeholder: "Enter input placeholder here"
validations:
required: true`,
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
placeholder: "${I18n.t(
"admin.form_templates.field_placeholders.placeholder"
)}"
validations:
# ${I18n.t("admin.form_templates.field_placeholders.validations")}`,
},
{
type: "textarea",
structure: `- type: textarea
attributes:
label: "Enter textarea label here"
description: "Enter textarea description here"
placeholder: "Enter textarea placeholder here"
validations:
required: true`,
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
placeholder: "${I18n.t(
"admin.form_templates.field_placeholders.placeholder"
)}"
validations:
# ${I18n.t("admin.form_templates.field_placeholders.validations")}`,
},
{
type: "dropdown",
structure: `- type: dropdown
choices:
- "Option 1"
- "Option 2"
- "Option 3"
- "${I18n.t("admin.form_templates.field_placeholders.choices.first")}"
- "${I18n.t("admin.form_templates.field_placeholders.choices.second")}"
- "${I18n.t("admin.form_templates.field_placeholders.choices.third")}"
attributes:
label: "Enter dropdown label here"
description: "Enter dropdown description here"
validations:
required: true`,
none_label: "${I18n.t(
"admin.form_templates.field_placeholders.none_label"
)}"
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
filterable: false
validations:
# ${I18n.t("admin.form_templates.field_placeholders.validations")}`,
},
{
type: "upload",
structure: `- type: upload
attributes:
file_types: "jpg, png, gif"
label: "Enter upload label here"
description: "Enter upload description here"`,
allow_multiple: false
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
validations:
# ${I18n.t("admin.form_templates.field_placeholders.validations")}`,
},
{
type: "multiselect",
structure: `- type: multiple_choice
structure: `- type: multi-select
choices:
- "Option 1"
- "Option 2"
- "Option 3"
- "${I18n.t("admin.form_templates.field_placeholders.choices.first")}"
- "${I18n.t("admin.form_templates.field_placeholders.choices.second")}"
- "${I18n.t("admin.form_templates.field_placeholders.choices.third")}"
attributes:
label: "Enter multiple choice label here"
description: "Enter multiple choice description here"
validations:
required: true`,
none_label: "${I18n.t(
"admin.form_templates.field_placeholders.none_label"
)}"
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
validations:
# ${I18n.t("admin.form_templates.field_placeholders.validations")}`,
},
];

View File

@@ -18,6 +18,14 @@ FormTemplate.reopenClass({
});
},
createOrUpdateTemplate(data) {
if (data.id) {
return this.updateTemplate(data.id, data);
} else {
return this.createTemplate(data);
}
},
deleteTemplate(id) {
return ajax(`/admin/customize/form-templates/${id}.json`, {
type: "DELETE",
@@ -35,4 +43,11 @@ FormTemplate.reopenClass({
return model.form_template;
});
},
validateTemplate(data) {
return ajax(`/admin/customize/form-templates/preview.json`, {
type: "GET",
data,
});
},
});

View File

@@ -2,7 +2,7 @@
<FormTemplate::InfoHeader />
{{#if this.model}}
<table class="form-templates--table grid">
<table class="form-templates__table grid">
<thead>
<th class="col heading">
{{i18n "admin.form_templates.list_table.headings.name"}}

View File

@@ -1,22 +0,0 @@
<DModalBody @rawTitle={{this.model.name}}>
<HighlightedCode @lang="yaml" @code={{this.model.template}} />
{{! ? TODO(@keegan): Perhaps add what places (ex. categories) the templates are active in }}
</DModalBody>
<div class="modal-footer">
<DButton
class="btn-primary"
@action={{this.editTemplate}}
@icon="pencil-alt"
@label="admin.form_templates.view_template.edit"
/>
<DButton
@action={{route-action "closeModal"}}
@label="admin.form_templates.view_template.close"
/>
<DButton
class="btn-danger"
@action={{this.deleteTemplate}}
@icon="trash-alt"
@label="admin.form_templates.view_template.delete"
/>
</div>

View File

@@ -0,0 +1,23 @@
<DModalBody
@class="form-templates__validation-options"
@title="admin.form_templates.validations_modal.modal_title"
>
<table>
<thead>
<tr>
{{#each this.tableHeaders as |header|}}
<th>{{header}}</th>
{{/each}}
</tr>
</thead>
<tbody>
{{#each this.validations as |item|}}
<tr>
<td><pre>{{item.key}}</pre></td>
<td>{{item.type}}</td>
<td>{{item.description}}</td>
</tr>
{{/each}}
</tbody>
</table>
</DModalBody>