0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-24 18:30:27 +01:00

First release candidate

Dieser Commit ist enthalten in:
Angus McLeod 2022-07-27 11:47:50 +01:00
Ursprung 11ff38ae90
Commit 60823cd87a
35 geänderte Dateien mit 788 neuen und 880 gelöschten Zeilen

Datei anzeigen

@ -46,8 +46,9 @@ jobs:
yarn prettier --list-different "assets/javascripts/{discourse,wizard}/**/*.{scss,js,es6}" ; \
fi
- name: Ember template lint
run: yarn ember-template-lint assets/javascripts
# Until templates are converted
#- name: Ember template lint
#run: yarn ember-template-lint assets/javascripts
- name: Rubocop
run: bundle exec rubocop .

Datei anzeigen

@ -132,3 +132,8 @@ jobs:
export COVERAGE=1
fi
bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}]
- name: Plugin QUnit
if: matrix.build_type == 'frontend'
run: bin/rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000']
timeout-minutes: 30

Datei anzeigen

@ -1,4 +1,4 @@
import CustomWizard from "../models/wizard";
import CustomWizard from "../models/custom-wizard";
import discourseComputed from "discourse-common/utils/decorators";
import Component from "@ember/component";
import { dasherize } from "@ember/string";

Datei anzeigen

@ -7,7 +7,7 @@ import { schedule } from "@ember/runloop";
import { cookAsync } from "discourse/lib/text";
import CustomWizard, {
updateCachedWizard,
} from "discourse/plugins/discourse-custom-wizard/discourse/models/wizard";
} from "discourse/plugins/discourse-custom-wizard/discourse/models/custom-wizard";
import { alias, not } from "@ember/object/computed";
const alreadyWarned = {};

Datei anzeigen

@ -1,9 +1,9 @@
import CustomWizard from "../../models/custom-wizard";
import CustomWizardAdmin from "../../models/custom-wizard-admin";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default {
setupComponent(attrs, component) {
CustomWizard.all()
CustomWizardAdmin.all()
.then((result) => {
component.set("wizardList", result);
})

Datei anzeigen

@ -20,29 +20,8 @@ export default Controller.extend({
"application/x-www-form-urlencoded",
]),
successCodes: selectKitContent([
100,
101,
102,
200,
201,
202,
203,
204,
205,
206,
207,
208,
226,
300,
301,
302,
303,
303,
304,
305,
306,
307,
308,
100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301,
302, 303, 303, 304, 305, 306, 307, 308,
]),
@discourseComputed(
@ -166,7 +145,7 @@ export default Controller.extend({
const originalTitle = this.get("api.originalTitle");
if (api.get("isNew") || (originalTitle && api.title !== originalTitle)) {
refreshList = true;
refreshList = true; // eslint-disable-line
}
if (api.get("isNew")) {

Datei anzeigen

@ -0,0 +1,55 @@
export default {
resource: "admin",
map() {
this.route(
"adminWizards",
{ path: "/wizards", resetNamespace: true },
function () {
this.route(
"adminWizardsWizard",
{ path: "/wizard/", resetNamespace: true },
function () {
this.route("adminWizardsWizardShow", {
path: "/:wizardId/",
resetNamespace: true,
});
}
);
this.route("adminWizardsCustomFields", {
path: "/custom-fields",
resetNamespace: true,
});
this.route(
"adminWizardsSubmissions",
{ path: "/submissions", resetNamespace: true },
function () {
this.route("adminWizardsSubmissionsShow", {
path: "/:wizardId/",
resetNamespace: true,
});
}
);
this.route(
"adminWizardsApi",
{ path: "/api", resetNamespace: true },
function () {
this.route("adminWizardsApiShow", {
path: "/:name",
resetNamespace: true,
});
}
);
this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
this.route("adminWizardsManager", {
path: "/manager",
resetNamespace: true,
});
}
);
},
};

Datei anzeigen

@ -9,55 +9,4 @@ export default function () {
});
}
);
this.route(
"adminWizards",
{ path: "/wizards", resetNamespace: true },
function () {
this.route(
"adminWizardsWizard",
{ path: "/wizard/", resetNamespace: true },
function () {
this.route("adminWizardsWizardShow", {
path: "/:wizardId/",
resetNamespace: true,
});
}
);
this.route("adminWizardsCustomFields", {
path: "/custom-fields",
resetNamespace: true,
});
this.route(
"adminWizardsSubmissions",
{ path: "/submissions", resetNamespace: true },
function () {
this.route("adminWizardsSubmissionsShow", {
path: "/:wizardId/",
resetNamespace: true,
});
}
);
this.route(
"adminWizardsApi",
{ path: "/api", resetNamespace: true },
function () {
this.route("adminWizardsApiShow", {
path: "/:name",
resetNamespace: true,
});
}
);
this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
this.route("adminWizardsManager", {
path: "/manager",
resetNamespace: true,
});
}
);
}

Datei anzeigen

@ -1,6 +0,0 @@
import { registerUnbound } from "discourse-common/lib/helpers";
import { dasherize } from "@ember/string";
registerUnbound("dasherize", function (string) {
return dasherize(string);
});

Datei anzeigen

@ -1,23 +0,0 @@
import { registerUnbound } from "discourse-common/lib/helpers";
import { longDate, relativeAge } from "discourse/lib/formatter";
import Handlebars from "handlebars";
export default registerUnbound("date-node", function (dt) {
if (typeof dt === "string") {
dt = new Date(dt);
}
if (dt) {
const attributes = {
title: longDate(dt),
"data-time": dt.getTime(),
"data-format": "tiny",
};
const finalString = `<span class="relative-date" title="${
attributes["title"]
}" data-time="${attributes["data-time"]}" data-format="${
attributes["data-format"]
}">${relativeAge(dt)}</span>`;
return new Handlebars.SafeString(finalString);
}
});

Datei anzeigen

@ -1,6 +0,0 @@
import { registerUnbound } from "discourse-common/lib/helpers";
import Handlebars from "handlebars";
export default registerUnbound("dir-span", function (str) {
return new Handlebars.SafeString(str);
});

Datei anzeigen

@ -1,17 +0,0 @@
import { htmlHelper } from "discourse-common/lib/helpers";
function renderSpinner(cssClass) {
var html = "<div class='spinner";
if (cssClass) {
html += " " + cssClass;
}
return html + "'></div>";
}
var spinnerHTML = renderSpinner();
export default htmlHelper((params) => {
const hash = params.hash;
return renderSpinner(hash && hash.size ? hash.size : undefined);
});
export { spinnerHTML, renderSpinner };

Datei anzeigen

@ -38,6 +38,7 @@ export default {
});
api.modifyClass("component:uppy-image-uploader", {
pluginId: "custom-wizard",
// Needed to ensure appEvents get registered when navigating between steps
@observes("id")
initOnStepChange() {

Datei anzeigen

@ -0,0 +1,227 @@
import EmberObject from "@ember/object";
import { buildProperties, mapped, present } from "../lib/wizard-json";
import { listProperties, snakeCase } from "../lib/wizard";
import wizardSchema from "../lib/wizard-schema";
import { Promise } from "rsvp";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
const CustomWizardAdmin = EmberObject.extend({
save(opts) {
return new Promise((resolve, reject) => {
let wizard = this.buildJson(this, "wizard");
if (wizard.error) {
reject(wizard);
}
let data = {
wizard,
};
if (opts.create) {
data.create = true;
}
ajax(`/admin/wizards/wizard/${wizard.id}`, {
type: "PUT",
contentType: "application/json",
data: JSON.stringify(data),
}).then((result) => {
if (result.backend_validation_error) {
reject(result);
} else {
resolve(result);
}
});
});
},
buildJson(object, type, result = {}) {
let objectType = object.type || null;
if (wizardSchema[type].types) {
if (!objectType) {
result.error = {
type: "required",
params: { type, property: "type" },
};
return result;
}
}
for (let property of listProperties(type, { objectType })) {
let value = object.get(property);
result = this.validateValue(property, value, object, type, result);
if (result.error) {
break;
}
if (mapped(property, type)) {
value = this.buildMappedJson(value);
}
if (value !== undefined && value !== null) {
result[property] = value;
}
}
if (!result.error) {
for (let arrayObjectType of Object.keys(
wizardSchema[type].objectArrays
)) {
let arraySchema = wizardSchema[type].objectArrays[arrayObjectType];
let objectArray = object.get(arraySchema.property);
if (arraySchema.required && !present(objectArray)) {
result.error = {
type: "required",
params: { type, property: arraySchema.property },
};
break;
}
result[arraySchema.property] = [];
for (let item of objectArray) {
let itemProps = this.buildJson(item, arrayObjectType);
if (itemProps.error) {
result.error = itemProps.error;
break;
} else {
result[arraySchema.property].push(itemProps);
}
}
}
}
return result;
},
validateValue(property, value, object, type, result) {
if (wizardSchema[type].required.indexOf(property) > -1 && !value) {
result.error = {
type: "required",
params: { type, property },
};
}
let dependent = wizardSchema[type].dependent[property];
if (dependent && value && !object[dependent]) {
result.error = {
type: "dependent",
params: { property, dependent },
};
}
if (property === "api_body") {
try {
value = JSON.parse(value);
} catch (e) {
result.error = {
type: "invalid",
params: { type, property },
};
}
}
return result;
},
buildMappedJson(value) {
if (typeof value === "string" || Number.isInteger(value)) {
return value;
}
if (!value || !value.length) {
return false;
}
let inputs = value;
let result = [];
inputs.forEach((inpt) => {
let input = {
type: inpt.type,
};
if (inpt.connector) {
input.connector = inpt.connector;
}
if (present(inpt.output)) {
input.output = inpt.output;
input.output_type = snakeCase(inpt.output_type);
input.output_connector = inpt.output_connector;
}
if (present(inpt.pairs)) {
input.pairs = [];
inpt.pairs.forEach((pr) => {
if (present(pr.key) && present(pr.value)) {
let pairParams = {
index: pr.index,
key: pr.key,
key_type: snakeCase(pr.key_type),
value: pr.value,
value_type: snakeCase(pr.value_type),
connector: pr.connector,
};
input.pairs.push(pairParams);
}
});
}
if (
(input.type === "assignment" && present(input.output)) ||
present(input.pairs)
) {
result.push(input);
}
});
if (!result.length) {
result = false;
}
return result;
},
remove() {
return ajax(`/admin/wizards/wizard/${this.id}`, {
type: "DELETE",
})
.then(() => this.destroy())
.catch(popupAjaxError);
},
});
CustomWizardAdmin.reopenClass({
all() {
return ajax("/admin/wizards/wizard", {
type: "GET",
})
.then((result) => {
return result.wizard_list;
})
.catch(popupAjaxError);
},
submissions(wizardId) {
return ajax(`/admin/wizards/submissions/${wizardId}`, {
type: "GET",
}).catch(popupAjaxError);
},
create(wizardJson = {}) {
const wizard = this._super.apply(this);
wizard.setProperties(buildProperties(wizardJson));
return wizard;
},
});
export default CustomWizardAdmin;

Datei anzeigen

@ -1,227 +1,127 @@
import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object";
import { buildProperties, mapped, present } from "../lib/wizard-json";
import { listProperties, snakeCase } from "../lib/wizard";
import wizardSchema from "../lib/wizard-schema";
import { Promise } from "rsvp";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators";
import getUrl from "discourse-common/lib/get-url";
import CustomWizardField from "./custom-wizard-field";
import CustomWizardStep from "./custom-wizard-step";
const CustomWizard = EmberObject.extend({
save(opts) {
return new Promise((resolve, reject) => {
let wizard = this.buildJson(this, "wizard");
@discourseComputed("steps.length")
totalSteps: (length) => length,
if (wizard.error) {
reject(wizard);
skip() {
if (this.required && !this.completed && this.permitted) {
return;
}
let data = {
wizard,
};
if (opts.create) {
data.create = true;
}
ajax(`/admin/wizards/wizard/${wizard.id}`, {
type: "PUT",
contentType: "application/json",
data: JSON.stringify(data),
}).then((result) => {
if (result.backend_validation_error) {
reject(result);
} else {
resolve(result);
}
});
});
CustomWizard.skip(this.id);
},
buildJson(object, type, result = {}) {
let objectType = object.type || null;
if (wizardSchema[type].types) {
if (!objectType) {
result.error = {
type: "required",
params: { type, property: "type" },
};
return result;
}
}
for (let property of listProperties(type, { objectType })) {
let value = object.get(property);
result = this.validateValue(property, value, object, type, result);
if (result.error) {
break;
}
if (mapped(property, type)) {
value = this.buildMappedJson(value);
}
if (value !== undefined && value !== null) {
result[property] = value;
}
}
if (!result.error) {
for (let arrayObjectType of Object.keys(
wizardSchema[type].objectArrays
)) {
let arraySchema = wizardSchema[type].objectArrays[arrayObjectType];
let objectArray = object.get(arraySchema.property);
if (arraySchema.required && !present(objectArray)) {
result.error = {
type: "required",
params: { type, property: arraySchema.property },
};
break;
}
result[arraySchema.property] = [];
for (let item of objectArray) {
let itemProps = this.buildJson(item, arrayObjectType);
if (itemProps.error) {
result.error = itemProps.error;
break;
} else {
result[arraySchema.property].push(itemProps);
}
}
}
}
return result;
},
validateValue(property, value, object, type, result) {
if (wizardSchema[type].required.indexOf(property) > -1 && !value) {
result.error = {
type: "required",
params: { type, property },
};
}
let dependent = wizardSchema[type].dependent[property];
if (dependent && value && !object[dependent]) {
result.error = {
type: "dependent",
params: { property, dependent },
};
}
if (property === "api_body") {
try {
value = JSON.parse(value);
} catch (e) {
result.error = {
type: "invalid",
params: { type, property },
};
}
}
return result;
},
buildMappedJson(value) {
if (typeof value === "string" || Number.isInteger(value)) {
return value;
}
if (!value || !value.length) {
return false;
}
let inputs = value;
let result = [];
inputs.forEach((inpt) => {
let input = {
type: inpt.type,
};
if (inpt.connector) {
input.connector = inpt.connector;
}
if (present(inpt.output)) {
input.output = inpt.output;
input.output_type = snakeCase(inpt.output_type);
input.output_connector = inpt.output_connector;
}
if (present(inpt.pairs)) {
input.pairs = [];
inpt.pairs.forEach((pr) => {
if (present(pr.key) && present(pr.value)) {
let pairParams = {
index: pr.index,
key: pr.key,
key_type: snakeCase(pr.key_type),
value: pr.value,
value_type: snakeCase(pr.value_type),
connector: pr.connector,
};
input.pairs.push(pairParams);
}
});
}
if (
(input.type === "assignment" && present(input.output)) ||
present(input.pairs)
) {
result.push(input);
}
});
if (!result.length) {
result = false;
}
return result;
},
remove() {
return ajax(`/admin/wizards/wizard/${this.id}`, {
type: "DELETE",
})
.then(() => this.destroy())
.catch(popupAjaxError);
restart() {
CustomWizard.restart(this.id);
},
});
CustomWizard.reopenClass({
all() {
return ajax("/admin/wizards/wizard", {
type: "GET",
})
skip(wizardId) {
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
.then((result) => {
return result.wizard_list;
CustomWizard.finished(result);
})
.catch(popupAjaxError);
},
submissions(wizardId) {
return ajax(`/admin/wizards/submissions/${wizardId}`, {
type: "GET",
}).catch(popupAjaxError);
restart(wizardId) {
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
.then(() => {
window.location.href = `/w/${wizardId}`;
})
.catch(popupAjaxError);
},
create(wizardJson = {}) {
const wizard = this._super.apply(this);
wizard.setProperties(buildProperties(wizardJson));
return wizard;
finished(result) {
let url = "/";
if (result.redirect_on_complete) {
url = result.redirect_on_complete;
}
window.location.href = getUrl(url);
},
build(wizardJson) {
if (!wizardJson) {
return null;
}
if (!wizardJson.completed && wizardJson.steps) {
wizardJson.steps = wizardJson.steps
.map((step) => {
const stepObj = CustomWizardStep.create(step);
stepObj.wizardId = wizardJson.id;
stepObj.fields.sort((a, b) => {
return parseFloat(a.number) - parseFloat(b.number);
});
let tabindex = 1;
stepObj.fields.forEach((f) => {
f.tabindex = tabindex;
if (["date_time"].includes(f.type)) {
tabindex = tabindex + 2;
} else {
tabindex++;
}
});
stepObj.fields = stepObj.fields.map((f) => {
f.wizardId = wizardJson.id;
f.stepId = stepObj.id;
return CustomWizardField.create(f);
});
return stepObj;
})
.sort((a, b) => {
return parseFloat(a.index) - parseFloat(b.index);
});
}
return CustomWizard.create(wizardJson);
},
});
export function findCustomWizard(wizardId, params = {}) {
let url = `/w/${wizardId}.json`;
let paramKeys = Object.keys(params).filter((k) => {
if (k === "wizard_id") {
return false;
}
return !!params[k];
});
if (paramKeys.length) {
url += "?";
paramKeys.forEach((k, i) => {
if (i > 0) {
url += "&";
}
url += `${k}=${params[k]}`;
});
}
return ajax(url).then((result) => {
return CustomWizard.build(result);
});
}
let _wizard_store;
export function updateCachedWizard(wizard) {
_wizard_store = wizard;
}
export function getCachedWizard() {
return _wizard_store;
}
export default CustomWizard;

Datei anzeigen

@ -1,11 +0,0 @@
import Site from "discourse/models/site";
import { getOwner } from "discourse-common/lib/get-owner";
export default Site.reopenClass({
// There is no site data actually loaded by the CW yet. This placeholder is
// needed by imported classes
createCurrent() {
const store = getOwner(this).lookup("service:store");
return store.createRecord("site", {});
},
});

Datei anzeigen

@ -1,164 +0,0 @@
import { default as computed } from "discourse-common/utils/decorators";
import getUrl from "discourse-common/lib/get-url";
import Field from "./field";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import Step from "./step";
import EmberObject from "@ember/object";
import Site from "./site";
const CustomWizard = EmberObject.extend({
@computed("steps.length")
totalSteps: (length) => length,
skip() {
if (this.required && !this.completed && this.permitted) {
return;
}
CustomWizard.skip(this.id);
},
restart() {
CustomWizard.restart(this.id);
},
});
CustomWizard.reopenClass({
skip(wizardId) {
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
.then((result) => {
CustomWizard.finished(result);
})
.catch(popupAjaxError);
},
restart(wizardId) {
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
.then(() => {
window.location.href = `/w/${wizardId}`;
})
.catch(popupAjaxError);
},
finished(result) {
let url = "/";
if (result.redirect_on_complete) {
url = result.redirect_on_complete;
}
window.location.href = getUrl(url);
},
build(wizardJson) {
if (!wizardJson) {
return null;
}
if (!wizardJson.completed && wizardJson.steps) {
wizardJson.steps = wizardJson.steps
.map((step) => {
const stepObj = Step.create(step);
stepObj.wizardId = wizardJson.id;
stepObj.fields.sort((a, b) => {
return parseFloat(a.number) - parseFloat(b.number);
});
let tabindex = 1;
stepObj.fields.forEach((f) => {
f.tabindex = tabindex;
if (["date_time"].includes(f.type)) {
tabindex = tabindex + 2;
} else {
tabindex++;
}
});
stepObj.fields = stepObj.fields.map((f) => {
f.wizardId = wizardJson.id;
f.stepId = stepObj.id;
return Field.create(f);
});
return stepObj;
})
.sort((a, b) => {
return parseFloat(a.index) - parseFloat(b.index);
});
}
if (wizardJson.categories) {
let subcatMap = {};
let categoriesById = {};
let categories = wizardJson.categories.map((c) => {
if (c.parent_category_id) {
subcatMap[c.parent_category_id] =
subcatMap[c.parent_category_id] || [];
subcatMap[c.parent_category_id].push(c.id);
}
return (categoriesById[c.id] = EmberObject.create(c));
});
// Associate the categories with their parents
categories.forEach((c) => {
let subcategoryIds = subcatMap[c.get("id")];
if (subcategoryIds) {
c.set(
"subcategories",
subcategoryIds.map((id) => categoriesById[id])
);
}
if (c.get("parent_category_id")) {
c.set("parentCategory", categoriesById[c.get("parent_category_id")]);
}
});
Site.currentProp("categories", categories);
Site.currentProp("listByActivity", categories);
Site.currentProp("categoriesById", categoriesById);
Site.currentProp(
"uncategorized_category_id",
wizardJson.uncategorized_category_id
);
}
return CustomWizard.create(wizardJson);
},
});
export function findCustomWizard(wizardId, params = {}) {
let url = `/w/${wizardId}`;
let paramKeys = Object.keys(params).filter((k) => {
if (k === "wizard_id") {
return false;
}
return !!params[k];
});
if (paramKeys.length) {
url += "?";
paramKeys.forEach((k, i) => {
if (i > 0) {
url += "&";
}
url += `${k}=${params[k]}`;
});
}
return ajax({ url, cache: false, dataType: "json" }).then((result) => {
return CustomWizard.build(result);
});
}
let _wizard_store;
export function updateCachedWizard(wizard) {
_wizard_store = wizard;
}
export function getCachedWizard() {
return _wizard_store;
}
export default CustomWizard;

Datei anzeigen

@ -1,9 +1,9 @@
import CustomWizard from "../models/custom-wizard";
import CustomWizardAdmin from "../models/custom-wizard-admin";
import DiscourseRoute from "discourse/routes/discourse";
export default DiscourseRoute.extend({
model() {
return CustomWizard.all();
return CustomWizardAdmin.all();
},
setupController(controller, model) {

Datei anzeigen

@ -1,11 +1,11 @@
import CustomWizard from "../models/custom-wizard";
import CustomWizardAdmin from "../models/custom-wizard-admin";
import DiscourseRoute from "discourse/routes/discourse";
const excludedMetaFields = ["route_to", "redirect_on_complete", "redirect_to"];
export default DiscourseRoute.extend({
model(params) {
return CustomWizard.submissions(params.wizardId);
return CustomWizardAdmin.submissions(params.wizardId);
},
setupController(controller, model) {

Datei anzeigen

@ -1,4 +1,4 @@
import CustomWizard from "../models/custom-wizard";
import CustomWizardAdmin from "../models/custom-wizard-admin";
import { ajax } from "discourse/lib/ajax";
import DiscourseRoute from "discourse/routes/discourse";
import I18n from "I18n";
@ -20,7 +20,9 @@ export default DiscourseRoute.extend({
setupController(controller, model) {
const parentModel = this.modelFor("adminWizardsWizard");
const wizard = CustomWizard.create(!model || model.create ? {} : model);
const wizard = CustomWizardAdmin.create(
!model || model.create ? {} : model
);
const fieldTypes = Object.keys(parentModel.field_types).map((type) => {
return {
id: type,

Datei anzeigen

@ -1,4 +1,4 @@
import { getCachedWizard } from "../models/wizard";
import { getCachedWizard } from "../models/custom-wizard";
import Route from "@ember/routing/route";
export default Route.extend({

Datei anzeigen

@ -1,5 +1,5 @@
import I18n from "I18n";
import { getCachedWizard } from "../models/wizard";
import { getCachedWizard } from "../models/custom-wizard";
import Route from "@ember/routing/route";
export default Route.extend({

Datei anzeigen

@ -1,4 +1,4 @@
import { findCustomWizard, updateCachedWizard } from "../models/wizard";
import { findCustomWizard, updateCachedWizard } from "../models/custom-wizard";
import I18n from "I18n";
import Route from "@ember/routing/route";
@ -50,6 +50,7 @@ export default Route.extend({
customWizard: true,
logoUrl: this.siteSettings.logo_small,
reset: null,
model,
});
const stepModel = this.modelFor("step");
@ -60,22 +61,17 @@ export default Route.extend({
) {
this.showDialog(model);
}
},
getWizardBackground() {
const wizard = this.controllerFor("custom-wizard").get("model");
return wizard ? wizard.get("background") : "";
const background = model.get("background");
if (background) {
document.body.style.background = background;
}
},
activate() {
if (!document.body.classList.contains("custom-wizard")) {
document.body.classList.add("custom-wizard");
}
const background = this.getWizardBackground();
if (background) {
document.body.style.background = background;
}
},
deactivate() {
@ -83,9 +79,6 @@ export default Route.extend({
document.body.classList.remove("custom-wizard");
}
const background = this.getWizardBackground();
if (background) {
document.body.style.background = "";
}
},
});

Datei anzeigen

@ -1,15 +1,23 @@
img.avatar {
border-radius: 50%;
vertical-align: middle;
}
div.ac-wrap {
box-sizing: border-box;
position: relative;
overflow: visible;
max-height: 150px;
min-height: 34px;
background-color: var(--secondary);
border: 1px solid var(--primary-medium);
padding: 5px 4px 1px 4px;
padding: 5px;
div.item {
float: left;
margin-bottom: 4px;
margin-right: 10px;
line-height: 1.6;
span {
height: 24px;
@ -46,20 +54,31 @@ div.ac-wrap {
color: var(--danger);
}
}
}
img.avatar {
border-radius: 50%;
vertical-align: middle;
}
.ac-loading {
position: absolute;
top: 7px;
right: 5px;
}
.autocomplete {
input {
margin-bottom: 0;
}
.autocomplete {
z-index: 999999;
position: absolute;
width: 240px;
width: inherit;
max-width: 240px;
box-sizing: border-box;
background-color: var(--secondary);
border: 1px solid var(--primary-low);
li,
.no-results {
padding: 10px;
}
ul {
list-style: none;
padding: 0;
@ -91,50 +110,30 @@ img.avatar {
color: var(--primary);
vertical-align: middle;
}
span.name {
font-size: $font-down-1;
vertical-align: middle;
margin-left: 5px;
color: var(--primary);
}
&.selected {
background-color: var(--tertiary);
span.username,
span.name {
color: var(--secondary);
}
&:hover {
}
&:hover:not(.selected) {
background-color: var(--highlight-low);
text-decoration: none;
}
}
}
}
}
.ac-wrap {
box-sizing: border-box;
position: relative;
.ac-loading {
position: absolute;
top: 7px;
right: 5px;
}
.item {
line-height: 1.6;
}
.autocomplete {
width: inherit;
left: 0 !important;
width: 100%;
top: 30px !important;
box-sizing: border-box;
li,
.no-results {
padding: 10px;
}
ul > label {
margin: 5px 0;

Datei anzeigen

@ -60,6 +60,7 @@ body.custom-wizard {
& > label.field-label {
order: 1;
font-weight: 400;
margin-bottom: 0;
}
& > .field-description {
@ -70,6 +71,7 @@ body.custom-wizard {
.url-field input {
width: 100%;
max-width: 500px;
font-size: 1.1487em;
padding: 6px;
transition: border-color 0.5s;
@ -136,10 +138,16 @@ body.custom-wizard {
position: absolute !important;
}
.d-time-input .select-kit.combo-box .select-kit-header {
.d-time-input {
.select-kit.combo-box {
width: 90px;
.select-kit-header {
font-size: 1.1487em;
padding: 6px;
}
}
}
.d-date-time-input {
display: flex;

Datei anzeigen

@ -2,6 +2,66 @@ en:
js:
wizard:
complete_custom: "Complete the {{name}}"
completed: "You have completed this wizard."
not_permitted: "You are not permitted to access this wizard."
none: "There is no wizard here."
return_to_site: "Return to {{siteName}}"
requires_login: "You need to be logged in to access this wizard."
reset: "Reset this wizard."
step_not_permitted: "You're not permitted to view this step."
incomplete_submission:
title: "Continue editing your draft submission from %{date}?"
resume: "Continue"
restart: "Start over"
x_characters:
one: "%{count} Character"
other: "%{count} Characters"
wizard_composer:
show_preview: "Preview Post"
hide_preview: "Edit Post"
quote_post_title: "Quote whole post"
bold_label: "B"
bold_title: "Strong"
bold_text: "strong text"
italic_label: "I"
italic_title: "Emphasis"
italic_text: "emphasized text"
link_title: "Hyperlink"
link_description: "enter link description here"
link_dialog_title: "Insert Hyperlink"
link_optional_text: "optional title"
link_url_placeholder: "http://example.com"
quote_title: "Blockquote"
quote_text: "Blockquote"
blockquote_text: "Blockquote"
code_title: "Preformatted text"
code_text: "indent preformatted text by 4 spaces"
paste_code_text: "type or paste code here"
upload_title: "Upload"
upload_description: "enter upload description here"
olist_title: "Numbered List"
ulist_title: "Bulleted List"
list_item: "List item"
toggle_direction: "Toggle Direction"
help: "Markdown Editing Help"
collapse: "minimize the composer panel"
abandon: "close composer and discard draft"
modal_ok: "OK"
modal_cancel: "Cancel"
cant_send_pm: "Sorry, you can't send a message to %{username}."
yourself_confirm:
title: "Did you forget to add recipients?"
body: "Right now this message is only being sent to yourself!"
realtime_validations:
similar_topics:
insufficient_characters: "Type a minimum 5 characters to start looking for similar topics"
insufficient_characters_categories: "Type a minimum 5 characters to start looking for similar topics in %{catLinks}"
results: "Your topic is similar to..."
no_results: "No similar topics."
loading: "Looking for similar topics..."
show: "show"
admin_js:
admin:
@ -483,78 +543,3 @@ en:
countrycode: "Please select a country."
coordinates: "Please complete the set of coordinates."
geo_location: "Search and select a result."
select_kit:
default_header_text: Select...
no_content: No matches found
filter_placeholder: Search...
filter_placeholder_with_any: Search or create...
create: "Create: '{{content}}'"
max_content_reached:
one: "You can only select {{count}} item."
other: "You can only select {{count}} items."
min_content_not_reached:
one: "Select at least {{count}} item."
other: "Select at least {{count}} items."
wizard:
completed: "You have completed this wizard."
not_permitted: "You are not permitted to access this wizard."
none: "There is no wizard here."
return_to_site: "Return to {{siteName}}"
requires_login: "You need to be logged in to access this wizard."
reset: "Reset this wizard."
step_not_permitted: "You're not permitted to view this step."
incomplete_submission:
title: "Continue editing your draft submission from %{date}?"
resume: "Continue"
restart: "Start over"
x_characters:
one: "%{count} Character"
other: "%{count} Characters"
wizard_composer:
show_preview: "Preview Post"
hide_preview: "Edit Post"
quote_post_title: "Quote whole post"
bold_label: "B"
bold_title: "Strong"
bold_text: "strong text"
italic_label: "I"
italic_title: "Emphasis"
italic_text: "emphasized text"
link_title: "Hyperlink"
link_description: "enter link description here"
link_dialog_title: "Insert Hyperlink"
link_optional_text: "optional title"
link_url_placeholder: "http://example.com"
quote_title: "Blockquote"
quote_text: "Blockquote"
blockquote_text: "Blockquote"
code_title: "Preformatted text"
code_text: "indent preformatted text by 4 spaces"
paste_code_text: "type or paste code here"
upload_title: "Upload"
upload_description: "enter upload description here"
olist_title: "Numbered List"
ulist_title: "Bulleted List"
list_item: "List item"
toggle_direction: "Toggle Direction"
help: "Markdown Editing Help"
collapse: "minimize the composer panel"
abandon: "close composer and discard draft"
modal_ok: "OK"
modal_cancel: "Cancel"
cant_send_pm: "Sorry, you can't send a message to %{username}."
yourself_confirm:
title: "Did you forget to add recipients?"
body: "Right now this message is only being sent to yourself!"
realtime_validations:
similar_topics:
insufficient_characters: "Type a minimum 5 characters to start looking for similar topics"
insufficient_characters_categories: "Type a minimum 5 characters to start looking for similar topics in %{catLinks}"
results: "Your topic is similar to..."
no_results: "No similar topics."
loading: "Looking for similar topics..."
show: "show"

Datei anzeigen

@ -1,53 +0,0 @@
# frozen_string_literal: true
describe "Sprockets: require_tree_discourse directive" do
let(:discourse_asset_path) {
"#{Rails.root}/app/assets/javascripts/"
}
let(:fixture_asset_path) {
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
}
let(:test_file_contents) {
"console.log('hello')"
}
let(:resolved_file_contents) {
File.read(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/resolved_js_file_contents.txt"
)
}
before do
@env ||= Sprockets::Environment.new
discourse_asset_path = "#{Rails.root}/app/assets/javascripts/"
fixture_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
@env.append_path(discourse_asset_path)
@env.append_path(fixture_asset_path)
@env.cache = {}
end
def create_tmp_folder_and_run(path, file_contents, &block)
dir = File.dirname(path)
unless File.directory?(dir)
FileUtils.mkdir_p(dir)
end
File.new(path, 'w')
File.write(path, file_contents)
yield block if block_given?
FileUtils.rm_r(dir)
end
it "includes assets from the discourse core" do
create_tmp_folder_and_run("#{discourse_asset_path}/sptest/test.js", test_file_contents) do
expect(@env.find_asset("require_tree_discourse_test.js").to_s).to eq(resolved_file_contents)
end
end
it "throws ArgumentError if path is empty" do
expect { @env.find_asset("require_tree_discourse_empty.js") }.to raise_error(CustomWizard::SprocketsEmptyPath).with_message("path cannot be empty")
end
it "throws ArgumentError if path is non non-existent" do
expect { @env.find_asset("require_tree_discourse_non_existant.js") }.to raise_error(CustomWizard::SprocketsFileNotFound)
end
end

Datei anzeigen

@ -1,9 +1,9 @@
# frozen_string_literal: true
describe CustomWizard::RealtimeValidationsController do
fab!(:validation_type) { "test_stub" }
fab!(:validation_type_stub) {
fab!(:user) { Fabricate(:user) }
let(:validation_type) { "test_stub" }
let(:validation_type_stub) {
{
types: [:text],
component: "similar-topics-validator",
@ -12,9 +12,8 @@ describe CustomWizard::RealtimeValidationsController do
}
}
before(:all) do
sign_in(Fabricate(:user))
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
before do
sign_in(user)
class CustomWizard::RealtimeValidation::TestStub
attr_accessor :user
@ -40,6 +39,7 @@ describe CustomWizard::RealtimeValidationsController do
end
it "gives the correct response for a given type" do
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
get '/realtime-validations.json', params: { type: validation_type }
expect(response.status).to eq(200)
expected_response = [
@ -50,11 +50,13 @@ describe CustomWizard::RealtimeValidationsController do
end
it "gives 400 error when no type is passed" do
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
get '/realtime-validations.json'
expect(response.status).to eq(400)
end
it "gives 400 error when a required additional param is missing" do
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
CustomWizard::RealtimeValidation.types[:test_stub][:required_params] = [:test1]
get '/realtime-validations.json', params: { type: validation_type }
expect(response.status).to eq(400)
@ -63,6 +65,7 @@ describe CustomWizard::RealtimeValidationsController do
end
it "gives 500 response code when a non existant type is passed" do
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
get '/realtime-validations.json', params: { type: "random_type" }
expect(response.status).to eq(500)
end

Datei anzeigen

@ -1,38 +1,42 @@
import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
import { test } from "qunit";
import { exists } from "../helpers/test";
import acceptance, {
import {
acceptance,
count,
query,
server,
visible,
} from "../helpers/acceptance";
import { allFieldsWizard, getWizard } from "../helpers/wizard";
exists,
visible
} from "discourse/tests/helpers/qunit-helpers";
import { allFieldsWizard } from "../helpers/wizard";
import tagsJson from "../fixtures/tags";
import usersJson from "../fixtures/users";
import { response } from "../pretender";
acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
acceptance("Field | Fields", function (needs) {
needs.pretender((server, helper) => {
server.get("/w/wizard.json", (request) => (helper.response(allFieldsWizard)));
server.get("/tags/filter/search", (request) => (helper.response({ results: tagsJson["tags"] })));
server.get("/u/search/users", (request) => (helper.response(usersJson)));
});
test("Text", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(exists(".wizard-field.text-field input.wizard-focusable"));
});
test("Textarea", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(
visible(".wizard-field.textarea-field textarea.wizard-focusable")
);
});
test("Composer", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(
visible(".wizard-field.composer-field .wizard-field-composer textarea")
);
assert.strictEqual(
count(".wizard-field.composer-field .d-editor-button-bar button"),
8
assert.ok(
exists(".wizard-field.composer-field .d-editor-button-bar button")
);
assert.ok(visible(".wizard-btn.toggle-preview"));
@ -50,19 +54,19 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
});
test("Text Only", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.text-only-field label.field-label"));
});
test("Date", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.date-field input.date-picker"));
await click(".wizard-field.date-field input.date-picker");
assert.ok(visible(".wizard-field.date-field .pika-single"));
});
test("Time", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit"));
await click(
".wizard-field.time-field .d-time-input .select-kit .select-kit-header"
@ -71,7 +75,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
});
test("Date Time", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(
visible(".wizard-field.date-time-field .d-date-time-input .select-kit")
);
@ -88,22 +92,22 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
});
test("Number", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.number-field input[type='number']"));
});
test("Checkbox", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']"));
});
test("Url", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.url-field input[type='text']"));
});
test("Upload", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(
visible(".wizard-field.upload-field label.wizard-btn-upload-file")
);
@ -111,7 +115,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
});
test("Dropdown", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.dropdown-field .single-select-header"));
await click(".wizard-field.dropdown-field .select-kit-header");
assert.strictEqual(
@ -121,10 +125,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
});
test("Tag", async function (assert) {
server.get("/tags/filter/search", () =>
response(200, { results: tagsJson["tags"] })
);
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.tag-field .multi-select-header"));
await click(".wizard-field.tag-field .select-kit-header");
assert.strictEqual(
@ -134,17 +135,16 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
});
test("Category", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.category-field .multi-select-header"));
await click(".wizard-field.category-field .select-kit-header");
assert.strictEqual(
count(".wizard-field.category-field .select-kit-collection li"),
5
assert.ok(
exists(".wizard-field.category-field .select-kit-collection li")
);
});
test("Group", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-field.group-field .single-select-header"));
await click(".wizard-field.group-field .select-kit-header");
assert.strictEqual(
@ -154,9 +154,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
});
test("User", async function (assert) {
server.get("/u/search/users", () => response(200, usersJson));
await visit("/wizard");
await visit("/w/wizard");
await fillIn(
".wizard-field.user-selector-field input.ember-text-field",
"a"

Datei anzeigen

@ -1,20 +1,33 @@
import { click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import { exists } from "../helpers/test";
import acceptance, { count, query, visible } from "../helpers/acceptance";
import { getWizard, stepNotPermitted, wizard } from "../helpers/wizard";
import { saveStep, update } from "../helpers/step";
import {
acceptance,
count,
query,
exists,
visible
} from "discourse/tests/helpers/qunit-helpers";
import { stepNotPermitted, wizard, update } from "../helpers/wizard";
acceptance("Step | Not permitted", function (needs) {
needs.pretender((server, helper) => {
server.get("/w/wizard.json", (request) => (helper.response(stepNotPermitted)));
});
acceptance("Step | Not permitted", [getWizard(stepNotPermitted)], function () {
test("Shows not permitted message", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(exists(".step-message.not-permitted"));
});
});
acceptance("Step | Step", [getWizard(wizard), saveStep(update)], function () {
acceptance("Step | Step", function (needs) {
needs.pretender((server, helper) => {
server.get("/w/wizard.json", (request) => (helper.response(wizard)));
server.put("/w/wizard/steps/:step_id", (request) => (helper.response(update)));
});
test("Renders the step", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.strictEqual(
query(".wizard-step-title p").textContent.trim(),
"Text"
@ -33,7 +46,7 @@ acceptance("Step | Step", [getWizard(wizard), saveStep(update)], function () {
});
test("Goes to the next step", async function (assert) {
await visit("/wizard");
await visit("/w/wizard");
assert.ok(visible(".wizard-step.step_1"), true);
await click(".wizard-btn.next");
assert.ok(visible(".wizard-step.step_2"), true);

Datei anzeigen

@ -0,0 +1,96 @@
import { visit, settled } from "@ember/test-helpers";
import { test } from "qunit";
import {
acceptance,
count,
query,
exists,
} from "discourse/tests/helpers/qunit-helpers";
import {
wizard,
wizardCompleted,
wizardNoUser,
wizardNotPermitted,
} from "../helpers/wizard";
acceptance("Wizard | Not logged in", function (needs) {
needs.pretender((server, helper) => {
server.get("/w/wizard.json", (request) => (helper.response(wizardNoUser)));
});
test("Wizard no access requires login", async function (assert) {
await visit("/w/wizard");
assert.ok(exists(".wizard-no-access.requires-login"));
});
});
acceptance(
"Wizard | Not permitted",
function (needs) {
needs.user();
needs.pretender((server, helper) => {
server.get("/w/wizard.json", (request) => (helper.response(wizardNotPermitted)));
});
test("Wizard no access not permitted", async function (assert) {
await visit("/w/wizard");
assert.ok(exists(".wizard-no-access.not-permitted"));
});
}
);
acceptance("Wizard | Completed", function (needs) {
needs.user();
needs.pretender((server, helper) => {
server.get("/w/wizard.json", (request) => (helper.response(wizardCompleted)));
});
test("Wizard no access completed", async function (assert) {
await visit("/w/wizard");
assert.ok(exists(".wizard-no-access.completed"));
});
});
acceptance("Wizard | Wizard", function (needs) {
needs.user();
needs.pretender((server, helper) => {
server.get("/w/wizard.json", (request) => {
return helper.response(wizard);
});
});
test("Starts", async function (assert) {
await visit("/w/wizard");
assert.ok(query(".wizard-column"), true);
});
test("Applies the body background color", async function (assert) {
await visit("/w/wizard");
assert.ok($("body")[0].style.background);
});
test("Renders the wizard form", async function (assert) {
await visit("/w/wizard");
assert.ok(exists(".wizard-column-contents .wizard-step"), true);
assert.ok(exists(".wizard-footer img"), true);
});
test("Renders the first step", async function (assert) {
await visit("/w/wizard");
assert.strictEqual(
query(".wizard-step-title p").textContent.trim(),
"Text"
);
assert.strictEqual(
query(".wizard-step-description p").textContent.trim(),
"Text inputs!"
);
assert.strictEqual(
query(".wizard-step-description p").textContent.trim(),
"Text inputs!"
);
assert.strictEqual(count(".wizard-step-form .wizard-field"), 6);
assert.ok(exists(".wizard-step-footer .wizard-progress"), true);
assert.ok(exists(".wizard-step-footer .wizard-buttons"), true);
});
});

Datei anzeigen

@ -1,73 +0,0 @@
import { visit } from "@ember/test-helpers";
import { test } from "qunit";
import { exists } from "../helpers/test";
import acceptance, { count, query, visible } from "../helpers/acceptance";
import {
getWizard,
wizard,
wizardCompleted,
wizardNoUser,
wizardNotPermitted,
} from "../helpers/wizard";
acceptance("Wizard | Not logged in", [getWizard(wizardNoUser)], function () {
test("Wizard no access requires login", async function (assert) {
await visit("/wizard");
assert.ok(exists(".wizard-no-access.requires-login"));
});
});
acceptance(
"Wizard | Not permitted",
[getWizard(wizardNotPermitted)],
function () {
test("Wizard no access not permitted", async function (assert) {
await visit("/wizard");
assert.ok(exists(".wizard-no-access.not-permitted"));
});
}
);
acceptance("Wizard | Completed", [getWizard(wizardCompleted)], function () {
test("Wizard no access completed", async function (assert) {
await visit("/wizard");
assert.ok(exists(".wizard-no-access.completed"));
});
});
acceptance("Wizard | Wizard", [getWizard(wizard)], function () {
test("Starts", async function (assert) {
await visit("/wizard");
assert.ok(query(".wizard-column"), true);
});
test("Applies the body background color", async function (assert) {
await visit("/wizard");
assert.ok($("body")[0].style.background);
});
test("Renders the wizard form", async function (assert) {
await visit("/wizard");
assert.ok(visible(".wizard-column-contents .wizard-step"), true);
assert.ok(visible(".wizard-footer img"), true);
});
test("Renders the first step", async function (assert) {
await visit("/wizard");
assert.strictEqual(
query(".wizard-step-title p").textContent.trim(),
"Text"
);
assert.strictEqual(
query(".wizard-step-description p").textContent.trim(),
"Text inputs!"
);
assert.strictEqual(
query(".wizard-step-description p").textContent.trim(),
"Text inputs!"
);
assert.strictEqual(count(".wizard-step-form .wizard-field"), 6);
assert.ok(visible(".wizard-step-footer .wizard-progress"), true);
assert.ok(visible(".wizard-step-footer .wizard-buttons"), true);
});
});

Datei anzeigen

@ -0,0 +1,47 @@
import wizardJson from "../fixtures/wizard";
import userJson from "../fixtures/user";
import categoriesJson from "../fixtures/categories";
import groupsJson from "../fixtures/groups";
import updateJson from "../fixtures/update";
import { cloneJSON } from "discourse-common/lib/object";
const wizardNoUser = cloneJSON(wizardJson);
const wizard = cloneJSON(wizardJson);
wizard.user = cloneJSON(userJson);
const wizardNotPermitted = cloneJSON(wizard);
wizardNotPermitted.permitted = false;
const wizardCompleted = cloneJSON(wizard);
wizardCompleted.completed = true;
wizard.start = "step_1";
wizard.resume_on_revisit = false;
wizard.submission_last_updated_at = "2022-03-11T20:00:18+01:00";
wizard.subscribed = false;
const stepNotPermitted = cloneJSON(wizard);
stepNotPermitted.steps[0].permitted = false;
const allFieldsWizard = cloneJSON(wizard);
allFieldsWizard.steps[0].fields = [
...allFieldsWizard.steps[0].fields,
...allFieldsWizard.steps[1].fields,
...allFieldsWizard.steps[2].fields,
];
allFieldsWizard.steps = [cloneJSON(allFieldsWizard.steps[0])];
allFieldsWizard.categories = cloneJSON(categoriesJson["categories"]);
allFieldsWizard.groups = cloneJSON(groupsJson["groups"]);
const update = cloneJSON(updateJson);
update.wizard = cloneJSON(wizardJson);
export {
wizardNoUser,
wizardNotPermitted,
wizardCompleted,
stepNotPermitted,
allFieldsWizard,
wizard,
update
};