Browse Source

Custom forms display steps, just don't save data yet

Angus McLeod 7 years ago
parent
commit
f8848d2b93
31 changed files with 339 additions and 111 deletions
  1. 19 8
      app/controllers/admin.rb
  2. 3 4
      app/controllers/steps.rb
  3. 1 1
      app/controllers/wizard.rb
  4. 4 3
      app/views/layouts/custom_wizard.html.erb
  5. 3 5
      assets/javascripts/discourse/components/wizard-custom-step.js.es6
  6. 2 2
      assets/javascripts/discourse/controllers/admin-wizard.js.es6
  7. 1 1
      assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
  8. 0 6
      assets/javascripts/discourse/initializers/wizard-edits.js.es6
  9. 85 19
      assets/javascripts/discourse/models/custom-wizard.js.es6
  10. 3 5
      assets/javascripts/discourse/routes/admin-wizard.js.es6
  11. 0 2
      assets/javascripts/discourse/templates/admin-wizard.hbs
  12. 1 1
      assets/javascripts/discourse/templates/admin-wizards-custom.hbs
  13. 1 0
      assets/javascripts/discourse/templates/components/wizard-custom-choice.hbs
  14. 1 1
      assets/javascripts/discourse/templates/components/wizard-custom-field.hbs
  15. 3 0
      assets/javascripts/wizard-custom.js
  16. 12 0
      assets/javascripts/wizard/application.hbs
  17. 20 0
      assets/javascripts/wizard/controllers/custom-step.js.es6
  18. 5 0
      assets/javascripts/wizard/custom-wizard.js.es6
  19. 56 3
      assets/javascripts/wizard/initializers/custom.js.es6
  20. 10 6
      assets/javascripts/wizard/models/custom-wizard.js.es6
  21. 0 7
      assets/javascripts/wizard/routes/application.js.es6
  22. 8 0
      assets/javascripts/wizard/routes/custom-index.js.es6
  23. 3 0
      assets/javascripts/wizard/routes/custom-step.js.es6
  24. 0 11
      assets/javascripts/wizard/routes/custom.js.es6
  25. 16 0
      assets/javascripts/wizard/templates/application.hbs
  26. 1 0
      assets/javascripts/wizard/templates/custom.step.hbs
  27. 0 0
      assets/stylesheets/custom_wizard.scss
  28. 11 15
      lib/builder.rb
  29. 6 5
      lib/wizard.rb
  30. 61 6
      plugin.rb
  31. 3 0
      public/desktop.css

+ 19 - 8
app/controllers/admin.rb

@@ -1,6 +1,6 @@
 class CustomWizard::AdminController < ::ApplicationController
-  before_filter :ensure_logged_in
-  before_filter :ensure_admin
+  before_action :ensure_logged_in
+  before_action :ensure_admin
 
   def index
     render nothing: true
@@ -11,9 +11,20 @@ class CustomWizard::AdminController < ::ApplicationController
 
     wizard = ::JSON.parse(params[:wizard])
 
-    wizard["id"] = SecureRandom.hex(8) if !wizard["id"]
-
-    PluginStore.set('custom_wizards', wizard["id"], wizard)
+    saved = false
+    if wizard["existing_id"] && rows = PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
+      rows.each do |r, i|
+        wizard = CustomWizard::Wizard.new(r.value)
+        if wizard.id = wizard["existing_id"]
+          r.update_all(key: wizard['id'], value: wizard)
+          saved = true
+        end
+      end
+    end
+
+    unless saved
+      PluginStore.set('custom_wizard', wizard["id"], wizard)
+    end
 
     render json: success_json
   end
@@ -21,7 +32,7 @@ class CustomWizard::AdminController < ::ApplicationController
   def remove
     params.require(:id)
 
-    PluginStore.remove('custom_wizards', params[:id])
+    PluginStore.remove('custom_wizard', params[:id])
 
     render json: success_json
   end
@@ -29,13 +40,13 @@ class CustomWizard::AdminController < ::ApplicationController
   def find
     params.require(:id)
 
-    wizard = PluginStore.get('custom_wizards', params[:id])
+    wizard = PluginStore.get('custom_wizard', params[:id])
 
     render json: success_json.merge(wizard: wizard)
   end
 
   def all
-    rows = PluginStoreRow.where(plugin_name: 'custom_wizards').order(:id)
+    rows = PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
 
     wizards = rows ? [*rows].map do |r|
       CustomWizard::Wizard.new(r.value)

+ 3 - 4
app/controllers/steps.rb

@@ -1,9 +1,9 @@
-class StepsController < ApplicationController
-  before_filter :ensure_logged_in
+class CustomWizard::StepsController < ApplicationController
+  before_action :ensure_logged_in
 
   def update
     wizard = CustomWizard::Builder.new(current_user, params[:wizard_id]).build
-    updater = wizard.create_updater(params[:id], params[:fields])
+    updater = wizard.create_updater(params[:step_id], params[:fields])
     updater.update
 
     if updater.success?
@@ -18,5 +18,4 @@ class StepsController < ApplicationController
       render json: { errors: errors }, status: 422
     end
   end
-
 end

+ 1 - 1
app/controllers/wizard.rb

@@ -6,7 +6,7 @@ class CustomWizard::WizardController < ::ApplicationController
   def index
     respond_to do |format|
       format.json do
-        wizard = CustomWizard::Builder.new(current_user, params[:name]).build
+        wizard = CustomWizard::Builder.new(current_user, params[:wizard_id]).build
         render_serialized(wizard, WizardSerializer)
       end
       format.html {}

+ 4 - 3
app/views/layouts/custom_wizard.html.erb

@@ -1,5 +1,6 @@
 <html>
   <head>
+    <link href="<%= Discourse.base_uri %>/plugins/discourse-custom-wizard/desktop.css" media="all" rel="stylesheet" data-target="desktop" type="text/css" />
     <%= discourse_stylesheet_link_tag :wizard, theme_key: nil %>
     <%= preload_script "ember_jquery" %>
     <%= preload_script "wizard-vendor" %>
@@ -16,12 +17,12 @@
     <title><%= t 'custom_wizard.title' %></title>
   </head>
 
-  <body class='wizard'>
-    <div id='wizard-main'></div>
+  <body class='custom-wizard'>
+    <div id='custom-wizard-main'></div>
 
     <script>
       (function() {
-        var wizard = require('wizard/wizard').default.create();
+        var wizard = require('discourse/plugins/discourse-custom-wizard/wizard/custom-wizard').default.create();
         wizard.start();
       })();
     </script>

+ 3 - 5
assets/javascripts/discourse/components/wizard-custom-step.js.es6

@@ -4,17 +4,15 @@ export default Ember.Component.extend({
   classNames: 'wizard-custom-step',
 
   @computed('step.fields.@each.id')
-  allowAddAction(stepFields) {
-    return stepFields.get('firstObject.id');
-  },
+  allowAddAction: stepFields => stepFields.get('firstObject.id'),
 
   actions: {
     addField() {
-      this.get('step.fields').pushObject(Ember.Object.create());
+      this.get('step.fields').pushObject(Ember.Object.create({ id: '', label: '' }));
     },
 
     addAction() {
-      this.get('step.actions').pushObject(Ember.Object.create());
+      this.get('step.actions').pushObject(Ember.Object.create({ id: '', label: '' }));
     },
 
     removeField(field) {

+ 2 - 2
assets/javascripts/discourse/controllers/admin-wizard.js.es6

@@ -13,10 +13,10 @@ export default Ember.Controller.extend({
     },
 
     addStep() {
-      this.get('model.steps').pushObject({
+      this.get('model.steps').pushObject(Ember.Object.create({
         fields: Ember.A(),
         actions: Ember.A()
-      });
+      }));
     },
 
     removeStep(step) {

+ 1 - 1
assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6

@@ -3,7 +3,7 @@ export default {
   map() {
     this.route('adminWizards', { path: '/wizards', resetNamespace: true }, function() {
       this.route('adminWizardsCustom', { path: '/custom', resetNamespace: true }, function() {
-        this.route('adminWizard', { path: '/:name', resetNamespace: true });
+        this.route('adminWizard', { path: '/:wizard_id', resetNamespace: true });
       });
     });
   }

+ 0 - 6
assets/javascripts/discourse/initializers/wizard-edits.js.es6

@@ -1,6 +0,0 @@
-export default {
-  name: 'wizard-edits',
-  initialize() {
-
-  }
-};

+ 85 - 19
assets/javascripts/discourse/models/custom-wizard.js.es6

@@ -1,22 +1,55 @@
-import { ajax } from 'discourse/lib/ajax';
 import { default as computed } from 'ember-addons/ember-computed-decorators';
+import { ajax } from 'discourse/lib/ajax';
 
 const CustomWizard = Discourse.Model.extend({
-  steps: Ember.A(),
+  init() {
+    const id = this.get('id');
+    if (id) this.set('existingId', id);
+  },
 
   @computed('name')
-  dasherizedName(name) {
-    return Ember.String.dasherize(name);
+  id(name) {
+    return name ? Ember.String.dasherize(name) : null;
   },
 
   save() {
-    const wizard = {
-      id: this.get('id'),
-      steps: this.get('steps').toArray(),
-      name: this.get('name')
+    const stepsObj = this.get('steps');
+    let steps = [];
+
+    stepsObj.forEach((s) => {
+      let step = {
+        id: Ember.String.dasherize(s.title),
+        title: s.title,
+        banner: s.banner,
+        description: s.description,
+        fields: [],
+        actions: []
+      };
+
+      const fields = s.get('fields');
+      fields.forEach((f) => {
+        f.set('id', Ember.String.dasherize(f.get('label')));
+        step['fields'].push(f);
+      });
+
+      s.actions.forEach((a) => {
+        a['id'] = Ember.String.dasherize(a.label);
+        step['actions'].push(a);
+      });
+
+      steps.push(step);
+    });
+
+    const id = this.get('id');
+    const name = this.get('name');
+    let wizard = { id, name, steps };
+
+    const existingId = this.get('existingId');
+    if (existingId && existingId !== id) {
+      wizard['existing_id'] = existingId;
     };
 
-    return ajax(`/admin/wizards/custom/save`, {
+    return ajax("/admin/wizards/custom/save", {
       type: 'PUT',
       data: {
         wizard: JSON.stringify(wizard)
@@ -25,7 +58,7 @@ const CustomWizard = Discourse.Model.extend({
   },
 
   remove() {
-    return ajax(`/admin/wizards/custom/remove`, {
+    return ajax("/admin/wizards/custom/remove", {
       type: 'DELETE',
       data: {
         id: this.get('id')
@@ -36,20 +69,53 @@ const CustomWizard = Discourse.Model.extend({
 
 CustomWizard.reopenClass({
   findAll() {
-    return ajax("/admin/wizards/custom/all").then(result => {
+    return ajax("/admin/wizards/custom/all", {
+      type: 'GET'
+    }).then(result => {
       return result.wizards.map(w => CustomWizard.create(w));
     });
   },
 
-  create() {
-    const wizard = this._super.apply(this, arguments);
-    const steps = wizard.get('steps');
+  create(w) {
+    const wizard = this._super.apply(this);
 
-    steps.forEach((s) => {
-      s.fields = Ember.A(s.fields);
-      s.fields.forEach((f) => f.choices = Ember.A(f.choices));
-      s.actions = Ember.A(s.actions);
-    });
+    let steps = Ember.A();
+    let props = { steps };
+
+    if (w) {
+      props['id'] = w.id; props['name'] = w.name;
+
+      if (w.steps) {
+        w.steps.forEach((s) => {
+          let fields = Ember.A();
+
+          s.fields.forEach((f) => {
+            let choices = Ember.A();
+
+            f.choices.forEach((c) => {
+              choices.pushObject(Ember.Object.create(c));
+            });
+
+            fields.pushObject(Ember.Object.create(f));
+          });
+
+          let actions = Ember.A();
+          s.actions.forEach((a) => {
+            actions.pushObject(Ember.Object.create(a));
+          });
+
+          steps.pushObject(Ember.Object.create({
+            id: s.id,
+            title: s.title,
+            description: s.description,
+            fields,
+            actions
+          }));
+        });
+      }
+    };
+
+    wizard.setProperties(props);
 
     return wizard;
   }

+ 3 - 5
assets/javascripts/discourse/routes/admin-wizard.js.es6

@@ -2,15 +2,13 @@ import CustomWizard from '../models/custom-wizard';
 
 export default Discourse.Route.extend({
   model(params) {
-    if (params.name === 'new') {
+    if (params.wizard_id === 'new') {
       this.set('new', true);
-      return CustomWizard.create({ name: '', steps: []});
+      return CustomWizard.create();
     }
-
     this.set('new', false);
 
-    const wizard = this.modelFor('admin-wizards-custom').findBy('dasherizedName', params.name);
-
+    const wizard = this.modelFor('admin-wizards-custom').findBy('id', params.wizard_id);
     if (!wizard) return this.transitionTo('adminWizardsCustom.index');
 
     return wizard;

+ 0 - 2
assets/javascripts/discourse/templates/admin-wizard.hbs

@@ -1,5 +1,4 @@
 <div class="form-horizontal">
-
   <div>
     <label for="name">{{i18n 'admin.wizard.name'}}</label>
     {{text-field name="name" value=model.name placeholderKey="admin.wizard.name_placeholder"}}
@@ -21,5 +20,4 @@
     {{/unless}}
     <span class="saving {{unless savingStatus 'hidden'}}">{{savingStatus}}</span>
   </div>
-
 </div>

+ 1 - 1
assets/javascripts/discourse/templates/admin-wizards-custom.hbs

@@ -3,7 +3,7 @@
     <ul>
       {{#each model as |w|}}
         <li>
-          {{#link-to "adminWizard" w.dasherizedName}}{{w.name}}{{/link-to}}
+          {{#link-to "adminWizard" w.id}}{{w.name}}{{/link-to}}
         </li>
       {{/each}}
     </ul>

+ 1 - 0
assets/javascripts/discourse/templates/components/wizard-custom-choice.hbs

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

+ 1 - 1
assets/javascripts/discourse/templates/components/wizard-custom-field.hbs

@@ -14,7 +14,7 @@
   {{#if isDropdown}}
     <span>{{i18n 'admin.wizard.field.choices_label'}}</span>
     {{#each field.choices as |c|}}
-      {{input type='text' value=c.label}}
+      {{wizard-custom-choice choice=c}}
     {{/each}}
     {{d-button action='addChoice' label='admin.wizard.field.add_choice'}}
   {{/if}}

+ 3 - 0
assets/javascripts/wizard-custom.js

@@ -1,3 +1,6 @@
+//= require ./wizard/custom-wizard
+//= require_tree ./wizard/controllers
 //= require_tree ./wizard/initializers
 //= require_tree ./wizard/models
 //= require_tree ./wizard/routes
+//= require_tree ./wizard/templates

+ 12 - 0
assets/javascripts/wizard/application.hbs

@@ -0,0 +1,12 @@
+{{#if showCanvas}}
+  {{wizard-canvas}}
+{{/if}}
+
+<div class='wizard-column'>
+  <div class='wizard-column-contents'>
+    {{outlet}}
+  </div>
+  <div class='wizard-footer'>
+    <div class='discourse-logo'></div>
+  </div>
+</div>

+ 20 - 0
assets/javascripts/wizard/controllers/custom-step.js.es6

@@ -0,0 +1,20 @@
+import StepController from 'wizard/controllers/step';
+import getUrl from 'discourse-common/lib/get-url';
+
+export default StepController.extend({
+  actions: {
+    goNext(response) {
+      const next = this.get('step.next');
+      if (response.refresh_required) {
+        const id = this.get('wizard.id');
+        document.location = getUrl(`/wizard/custom/${id}/steps/${next}`);
+      } else {
+        this.transitionToRoute('custom.step', next);
+      }
+    },
+
+    goBack() {
+      this.transitionToRoute('custom.step', this.get('step.previous'));
+    }
+  }
+});

+ 5 - 0
assets/javascripts/wizard/custom-wizard.js.es6

@@ -0,0 +1,5 @@
+import WizardApplication from 'wizard/wizard';
+
+export default WizardApplication.extend({
+  rootElement: '#custom-wizard-main'
+});

+ 56 - 3
assets/javascripts/wizard/initializers/custom.js.es6

@@ -1,13 +1,66 @@
-import Router from 'wizard/router';
 
 export default {
   name: 'custom-routes',
 
-  initialize() {
+  initialize(container, app) {
+    if (app.get('rootElement') !== '#custom-wizard-main') return;
+
+    const WizardApplicationRoute = requirejs('wizard/routes/application').default;
+    const findCustomWizard = requirejs('discourse/plugins/discourse-custom-wizard/wizard/models/custom').findCustomWizard;
+    const Router = requirejs('wizard/router').default;
+    const ajax = requirejs('wizard/lib/ajax').ajax;
+    const StepRoute = requirejs('wizard/routes/step').default;
+    const StepModel = requirejs('wizard/models/step').default;
+
     Router.map(function() {
-      this.route('custom', { path: '/custom/:name' }, function() {
+      this.route('custom', { path: '/custom/:id' }, function() {
         this.route('step', { path: '/steps/:step_id' });
       });
     });
+
+    WizardApplicationRoute.reopen({
+      model() {
+        const customParams = this.paramsFor('custom');
+        return findCustomWizard(customParams.id);
+      },
+
+      afterModel(model) {
+        return ajax({
+          url: `/site/basic-info`,
+          type: 'GET',
+        }).then((result) => {
+          return model.set('siteInfo', result);
+        });
+      },
+
+      setupController(controller, model) {
+        controller.setProperties({
+          customWizard: true,
+          siteInfo: model.get('siteInfo')
+        });
+      }
+    });
+
+    StepModel.reopen({
+      save() {
+        const fields = {};
+        this.get('fields').forEach(f => fields[f.id] = f.value);
+        return ajax({
+          url: `/wizard/custom/${this.get('wizardId')}/steps/${this.get('id')}`,
+          type: 'PUT',
+          data: { fields }
+        }).catch(response => {
+          response.responseJSON.errors.forEach(err => this.fieldError(err.field, err.description));
+          throw response;
+        });
+      }
+    });
+
+    StepRoute.reopen({
+      afterModel(model) {
+        const wizard = this.modelFor('application');
+        return model.set("wizardId", wizard.id);
+      }
+    });
   }
 };

+ 10 - 6
assets/javascripts/wizard/models/custom-wizard.js.es6

@@ -1,22 +1,26 @@
-import Step from 'wizard/models/step';
+import { default as computed } from 'ember-addons/ember-computed-decorators';
 import WizardField from 'wizard/models/wizard-field';
 import { ajax } from 'wizard/lib/ajax';
-import computed from 'ember-addons/ember-computed-decorators';
+import Step from 'wizard/models/step';
 
 const CustomWizard = Ember.Object.extend({
   @computed('steps.length')
   totalSteps: length => length
 });
 
-export function findCustomWizard(name) {
-  return ajax({ url: `/wizard/custom/${name}.json` }).then(response => {
-    const wizard = response.wizard;
+export function findCustomWizard(wizardId) {
+  return ajax({ url: `/wizard/custom/${wizardId}` }).then(result => {
+    const wizard = result.wizard;
     wizard.steps = wizard.steps.map(step => {
       const stepObj = Step.create(step);
       stepObj.fields = stepObj.fields.map(f => WizardField.create(f));
       return stepObj;
     });
 
+    console.log(wizard)
+
     return CustomWizard.create(wizard);
   });
-}
+};
+
+export default CustomWizard;

+ 0 - 7
assets/javascripts/wizard/routes/application.js.es6

@@ -1,7 +0,0 @@
-import { findCustomWizard } from '../models/custom-wizard';
-
-export default Ember.Route.extend({
-  model(params) {
-    return findCustomWizard(params.name);
-  }
-});

+ 8 - 0
assets/javascripts/wizard/routes/custom-index.js.es6

@@ -0,0 +1,8 @@
+import IndexRoute from 'wizard/routes/index';
+
+export default IndexRoute.extend({
+  beforeModel() {
+    const appModel = this.modelFor('application');
+    this.replaceWith('custom.step', appModel.start);
+  }
+});

+ 3 - 0
assets/javascripts/wizard/routes/custom-step.js.es6

@@ -0,0 +1,3 @@
+import StepRoute from 'wizard/routes/step';
+
+export default StepRoute.extend();

+ 0 - 11
assets/javascripts/wizard/routes/custom.js.es6

@@ -1,11 +0,0 @@
-import { findCustomWizard } from '../models/custom-wizard';
-
-export default Ember.Route.extend({
-  model(params) {
-    return findCustomWizard(params.name);
-  },
-
-  afterModel(model) {
-    this.replaceWith('step', model.start);
-  }
-});

+ 16 - 0
assets/javascripts/wizard/templates/application.hbs

@@ -0,0 +1,16 @@
+{{#if showCanvas}}
+  {{wizard-canvas}}
+{{/if}}
+
+<div class='wizard-column'>
+  <div class='wizard-column-contents'>
+    {{outlet}}
+  </div>
+  <div class='wizard-footer'>
+    {{#if customWizard}}
+      <img src="{{siteInfo.logo_small_url}}" style="background-image: initial; width: 33px; height: 33px;"/>
+    {{else}}
+      <div class='discourse-logo'></div>
+    {{/if}}
+  </div>
+</div>

+ 1 - 0
assets/javascripts/wizard/templates/custom.step.hbs

@@ -0,0 +1 @@
+{{wizard-step step=step wizard=wizard goNext="goNext" goBack="goBack"}}

assets/stylesheets/custom-wizard.scss → assets/stylesheets/custom_wizard.scss


+ 11 - 15
lib/builder.rb

@@ -1,31 +1,27 @@
 class CustomWizard::Builder
-  def initialize(user, wizard_name)
-    rows = PluginStoreRow.where(plugin_name: 'custom_wizards')
-    return if !rows
-
-    [*rows].each do |r|
-      wizard = CustomWizard::Wizard.new(r.value)
-      @template = wizard if wizard.name.dasherize.downcase == wizard_name
-    end
-
+  def initialize(user, wizard_id)
+    data = PluginStore.get('custom_wizard', wizard_id)
+    @custom_wizard = CustomWizard::Wizard.new(data)
     @wizard = Wizard.new(user)
+    @wizard.id = wizard_id
   end
 
   def build
-    @template.steps.each do |s|
-      @wizard.append_step(s['title']) do |step|
-
+    @custom_wizard.steps.each do |s|
+      @wizard.append_step(s['id']) do |step|
+        step.title = s['title'] if s['title']
         step.banner = s['banner'] if s['banner']
 
         s['fields'].each do |f|
           field = step.add_field(id: f['id'],
                                  type: f['type'],
-                                 required: f['required'],
-                                 value: f['value'])
+                                 label: f['label'],
+                                 description: f['description'],
+                                 required: f['required'])
 
           if f['type'] == 'dropdown'
             f['choices'].each do |c|
-              field.add_choice(c)
+              field.add_choice(c['id'], label: c['label'])
             end
           end
         end

+ 6 - 5
lib/wizard.rb

@@ -1,11 +1,12 @@
 class CustomWizard::Wizard
 
-  attr_reader :name, :steps
+  attr_reader :id, :name, :steps, :custom
 
   def initialize(data)
-    parsed = ::JSON.parse(data)
-    @id = parsed['id']
-    @name = parsed['name']
-    @steps = parsed['steps']
+    data = data.is_a?(String) ? ::JSON.parse(data) : data
+    @id = data['id']
+    @name = data['name']
+    @steps = data['steps']
+    @custom = true
   end
 end

+ 61 - 6
plugin.rb

@@ -3,7 +3,7 @@
 # version: 0.1
 # authors: Angus McLeod
 
-register_asset 'stylesheets/custom-wizard.scss'
+register_asset 'stylesheets/custom_wizard.scss'
 
 config = Rails.application.config
 config.assets.paths << Rails.root.join("plugins", "discourse-custom-wizard", "assets", "javascripts")
@@ -24,10 +24,10 @@ after_initialize do
   load File.expand_path('../app/controllers/admin.rb', __FILE__)
 
   CustomWizard::Engine.routes.draw do
-    get ':name' => 'wizard#index'
-    get ':name/steps' => 'steps#index'
-    get ':name/steps/:id' => 'wizard#index'
-    put ':name/steps/:id' => 'steps#update'
+    get ':wizard_id' => 'wizard#index'
+    get ':wizard_id/steps' => 'steps#index'
+    get ':wizard_id/steps/:step_id' => 'wizard#index'
+    put ':wizard_id/steps/:step_id' => 'steps#update'
   end
 
   require_dependency 'admin_constraint'
@@ -40,9 +40,64 @@ after_initialize do
       get 'admin/wizards/custom' => 'admin#index'
       get 'admin/wizards/custom/new' => 'admin#index'
       get 'admin/wizards/custom/all' => 'admin#all'
-      get 'admin/wizards/custom/:id' => 'admin#find'
+      get 'admin/wizards/custom/:wizard_id' => 'admin#find'
       put 'admin/wizards/custom/save' => 'admin#save'
       delete 'admin/wizards/custom/remove' => 'admin#remove'
     end
   end
+
+  class ::Wizard
+    attr_accessor :id
+  end
+
+  class ::Wizard::Step
+    attr_accessor :title
+  end
+
+  ::Wizard::Field.class_eval do
+    attr_reader :label, :description
+
+    def initialize(attrs)
+      attrs = attrs || {}
+
+      @id = attrs[:id]
+      @type = attrs[:type]
+      @required = !!attrs[:required]
+      @label = attrs[:label]
+      @description = attrs[:description]
+      @value = attrs[:value]
+      @choices = []
+    end
+  end
+
+  add_to_serializer(:wizard, :id) { object.id }
+
+  ::WizardStepSerializer.class_eval do
+    def title
+      if object.title
+        object.title
+      else
+        I18n.t("#{i18n_key}.title", default: '')
+      end
+    end
+  end
+
+  ::WizardFieldSerializer.class_eval do
+    def label
+      puts "LABEL: #{object.label}"
+      if object.label
+        object.label
+      else
+        I18n.t("#{i18n_key}.label", default: '')
+      end
+    end
+
+    def description
+      if object.description
+        object.description
+      else
+        I18n.t("#{i18n_key}.description", default: '')
+      end
+    end
+  end
 end

+ 3 - 0
public/desktop.css

@@ -0,0 +1,3 @@
+.custom-wizard {
+  background-color: #f4f4f4;
+}