FEATURE: editable badge groups

This commit is contained in:
Sam 2014-07-27 18:22:01 +10:00
parent 0ab456b647
commit 1a6aa07611
12 changed files with 192 additions and 9 deletions

View file

@ -7,6 +7,7 @@
@module Discourse
**/
export default Ember.ArrayController.extend({
needs: ['modal'],
itemController: 'admin-badge',
queryParams: ['badgeId'],
badgeId: Em.computed.alias('selectedId'),

View file

@ -0,0 +1,80 @@
export default Ember.Controller.extend({
needs: ['modal'],
modelChanged: function(){
var grouping = Em.Object.extend({});
var model = this.get('model');
var copy = Em.A();
if(model){
model.forEach(function(o){
copy.pushObject(grouping.create(o));
});
}
this.set('workingCopy', copy);
}.observes('model'),
moveItem: function(item, delta){
var copy = this.get('workingCopy');
var index = copy.indexOf(item);
if (index + delta < 0 || index + delta >= copy.length){
return;
}
copy.removeAt(index);
copy.insertAt(index+delta, item);
},
actions: {
up: function(item){
this.moveItem(item, -1);
},
down: function(item){
this.moveItem(item, 1);
},
"delete": function(item){
this.get('workingCopy').removeObject(item);
},
cancel: function(){
this.set('model', null);
this.set('workingCopy', null);
this.send('closeModal');
},
edit: function(item){
item.set("editing", true);
},
save: function(item){
item.set("editing", false);
},
add: function(){
var obj = Em.Object.create({editing: true, name: "Enter Name"});
this.get('workingCopy').pushObject(obj);
},
saveAll: function(){
var self = this;
var items = this.get('workingCopy');
var groupIds = items.map(function(i){return i.get("id") || -1});
var names = items.map(function(i){return i.get("name")});
Discourse.ajax('/admin/badges/badge_groupings',{
data: {ids: groupIds, names: names},
method: 'POST'
}).then(function(data){
items = self.get("model");
items.clear();
data.badge_groupings.forEach(function(g){
items.pushObject(Em.Object.create(g));
});
self.set('model', null);
self.set('workingCopy', null);
self.send('closeModal');
},function(){
// TODO we can do better
bootbox.alert("Something went wrong");
});
}
}
});

View file

@ -1,7 +1,8 @@
Discourse.AdminBadgesRoute = Discourse.Route.extend({
setupController: function(controller) {
Discourse.ajax('/admin/badges.json').then(function(json){
controller.set('badgeGroupings', json.badge_groupings);
controller.set('badgeGroupings', Em.A(json.badge_groupings));
controller.set('badgeTypes', json.badge_types);
controller.set('protectedSystemFields', json.admin_badges.protected_system_fields);
var triggers = [];
@ -11,6 +12,12 @@ Discourse.AdminBadgesRoute = Discourse.Route.extend({
controller.set('badgeTriggers', triggers);
controller.set('model', Discourse.Badge.createFromJson(json));
});
},
actions: {
editGroupings: function(model){
Discourse.Route.showModal(this, 'admin_edit_badge_groupings', model);
}
}
});

View file

@ -53,6 +53,7 @@
content=controller.badgeGroupings
optionValuePath="content.id"
optionLabelPath="content.name"}}
&nbsp;<button {{action editGroupings controller.badgeGroupings}}><i class="fa fa-pencil"></i></button>
</div>

View file

@ -0,0 +1,28 @@
<div class="modal-body">
<div>
<ul class='badge-groupings'>
{{#each workingCopy}}
<li>
{{#if editing}}
{{input value=this.name}}
<button {{action save this}}><i class="fa fa-check"></i></button>
{{else}}
{{this.name}}
{{/if}}
<div class='actions'>
<button {{action edit this}}><i class="fa fa-pencil"></i></button>
<button {{action up this}}><i class="fa fa-toggle-up"></i></button>
<button {{action down this}}><i class="fa fa-toggle-down"></i></button>
<button {{action delete this}}><i class="fa fa-times"></i></button>
</div>
</li>
{{/each}}
</ul>
</div>
<button class='btn' {{action add}}>{{i18n admin.badges.new}}</button>
</div>
<div class="modal-footer">
<button class='btn btn-primary' {{action saveAll}} {{bind-attr disabled="submitDisabled"}}>{{i18n admin.badges.save}}</button>
<a {{action cancel}}>{{i18n cancel}}</a>
</div>

View file

@ -0,0 +1,5 @@
Discourse.AdminEditBadgeGroupingsView = Discourse.ModalBodyView.extend({
templateName: 'admin/templates/modal/admin_edit_badge_groupings',
title: I18n.t('admin.badges.badge_groupings.modal_title')
});

View file

@ -1205,3 +1205,21 @@ and (max-width : 500px) {
}
}
.badge-groupings {
list-style: none;
margin: 0;
padding: 10px 3px;
li {
padding: 6px 0;
width: 600px;
border-bottom: 1px solid #dfdfdf;
}
.actions {
font-size: 17px;
float: right;
a {
margin-left: 5px;
}
}
}

View file

@ -2,8 +2,8 @@ class Admin::BadgesController < Admin::AdminController
def index
data = {
badge_types: BadgeType.all.to_a,
badge_groupings: BadgeGrouping.all.to_a,
badge_types: BadgeType.all.order(:id).to_a,
badge_groupings: BadgeGrouping.all.order(:position).to_a,
badges: Badge.all.to_a,
protected_system_fields: Badge.protected_system_fields,
triggers: Badge.trigger_hash
@ -20,8 +20,24 @@ class Admin::BadgesController < Admin::AdminController
render_serialized(badge_types, BadgeTypeSerializer, root: "badge_types")
end
def badge_groupings
badge_groupings = BadgeGrouping.all.to_a
def save_badge_groupings
badge_groupings = BadgeGrouping.all.order(:position).to_a
ids = params[:ids].map(&:to_i)
params[:names].each_with_index do |name,index|
id = ids[index].to_i
group = badge_groupings.find{|b| b.id == id} || BadgeGrouping.new()
group.name = name
group.position = index
group.save
end
badge_groupings.each do |g|
g.destroy unless g.system? || ids.include?(g.id)
end
badge_groupings = BadgeGrouping.all.order(:position).to_a
render_serialized(badge_groupings, BadgeGroupingSerializer, root: "badge_groupings")
end

View file

@ -8,6 +8,10 @@ class BadgeGrouping < ActiveRecord::Base
has_many :badges
def system?
id && id < 5
end
def default_position=(pos)
position ||= pos
end

View file

@ -1889,6 +1889,8 @@ en:
description: Description
badge_type: Badge Type
badge_grouping: Group
badge_groupings:
modal_title: Badge Groupings
granted_by: Granted By
granted_at: Granted At
save: Save

View file

@ -146,7 +146,7 @@ Discourse::Application.routes.draw do
resources :badges, constraints: AdminConstraint.new do
collection do
get "types" => "badges#badge_types"
get "groupings" => "badges#badge_groupings"
post "badge_groupings" => "badges#save_badge_groupings"
post "preview" => "badges#preview"
end
end

View file

@ -1,14 +1,35 @@
require 'spec_helper'
describe Admin::BadgesController do
it "is a subclass of AdminController" do
(Admin::BadgesController < Admin::AdminController).should be_true
end
context "while logged in as an admin" do
let!(:user) { log_in(:admin) }
let!(:badge) { Fabricate(:badge) }
context '.save_badge_groupings' do
it 'can save badge groupings' do
groupings = BadgeGrouping.all.order(:position).to_a
groupings << BadgeGrouping.new(name: 'Test 1')
groupings << BadgeGrouping.new(name: 'Test 2')
groupings.shuffle!
names = groupings.map{|g| g.name}
ids = groupings.map{|g| g.id.to_s}
xhr :post, :save_badge_groupings, ids: ids, names: names
groupings2 = BadgeGrouping.all.order(:position).to_a
groupings2.map{|g| g.name}.should == names
(groupings.map(&:id) - groupings2.map{|g| g.id}).compact.should be_blank
::JSON.parse(response.body)["badge_groupings"].length.should == groupings2.length
end
end
context '.badge_types' do
it 'returns success' do
xhr :get, :badge_types