[refs #194] labels manage

This commit is contained in:
Alexander Machehin 2012-02-23 20:48:31 +06:00
parent ecb29d1762
commit 9e5e7cd5e4
21 changed files with 227 additions and 281 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -102,12 +102,12 @@ var el = el,
el.next().find("input").eq(0).attr("disabled","disabled");
}
el.next().bind("mousedown", function(e) {
el.next().bind("mousedown", function(e) {
changeRadio(jQuery(this));
$(this).find("input:radio").change();
$(this).find("input:radio").change();
});
if(jQuery.browser.msie) el.next().find("input").eq(0).bind("click", function(e) { changeVisualRadio(jQuery(this)) });
if(jQuery.browser.msie) el.next().find("input").eq(0).bind("click", function(e) { changeVisualRadio(jQuery(this)) });
else el.next().find("input").eq(0).bind("change", function(e) { changeVisualRadio(jQuery(this)) });
el.remove();
}

View File

@ -1,179 +1,138 @@
$(document).ready(function() {
var locale = {};
$("#closed-switcher").live('click', function() {
if ($("#blue-switch-select").css("margin-left") != "130px") {
$("#blue-switch-select").animate({"margin-left": "+=130px"}, "fast");
$("#table1").fadeOut(0);
$("#table2").fadeIn("slow");
var status = 'closed'
$('#issues_status').val('closed');
}
else {
$("#blue-switch-select").animate({"margin-left": "-=130px"}, "fast");
$("#table2").fadeOut(0);
$("#table1").fadeIn("slow");
var status = 'open'
$('#issues_status').val('open');
}
var form = $('#filter_issues');
$.ajax({
type: "GET",
url: form.attr("action"),
data: form.serialize() + '&status=' + status,
success: function(data){
$('article').html(data);
$(".niceRadio").each(function() { changeRadioStart(jQuery(this)) });
}
});
return send_request('GET', form.attr("action"));
});
});
function showEditLabels() {
$("#labels-stock").fadeOut(0);
$("#labels-edit").fadeIn("slow");
};
function hideEditLabels() {
$("#labels-edit").fadeOut(0);
$("#labels-stock").fadeIn("slow");
};
$(document).ready(function() {
$("#myTable").tablesorter({
headers: {
1: {
sorter: false
}
}
});
});
$(document).ready(function() {
$("#myTable2").tablesorter({
headers: {
1: {
sorter: false
}
}
});
});
$(document).ready(function() {
$("#manage-labels").click(function() {
$("#labels-stock").fadeOut(0);
$("#labels-edit").fadeIn("slow");
});
});
$(document).ready(function() {
$("div.delete").click(function() {
var div = "#label-"+this.id;
$(div).fadeOut("slow");
});
});
$(document).ready(function() {
$("div.div-tracker-lables").click(function() {
var flag = this.id;
flag = flag.replace("label-","flag-");
var bg = $("#"+flag).css("background-color");
if ($(this).css("background-color") != bg) {
$(this).css("background-color",bg);
$(this).css("color","#FFFFFF");
var labels = document.getElementsByName("label");
var rows = document.getElementsByName("row");
var arrayLabels;
var rowState = 0;
for (var r in rows) {
for (var l in labels) {
var ro = document.getElementById(rows[r].id);
var cls = ro.className;
var clsLabel = labels[l].id.split("label-")[1];
if (($("#"+labels[l].id).css("background-color") != "rgb(247, 247, 247)")&&($("#"+labels[l].id).css("background-color") != "transparent")) {
if (cls.indexOf(clsLabel) != -1) {
rowState = 1;
}
}
}
if (rowState == 1) {
showRow(rows[r].id);
rowState = 0;
$("#manage-labels").live('click', function () {
var toggled = $(this).data('toggled');
$(this).data('toggled', !toggled);
if (!toggled) {
showEditLabels();
}
else {
hideRow(rows[r].id);
hideEditLabels();
}
}
} else {
$(this).css("background-color","rgb(247, 247, 247)");
$(this).css("color","#565657");
var labels = document.getElementsByName("label");
var rows = document.getElementsByName("row");
var rowState = 0;
var labelState = 0;
for (var l in labels) {
if (($("#"+labels[l].id).css("background-color") != "rgb(247, 247, 247)")&&($("#"+labels[l].id).css("background-color") != "transparent")) {
labelState = 1;
}
}
if (labelState == 1) {
for (var r in rows) {
for (var l in labels) {
var ro = document.getElementById(rows[r].id);
var cls = ro.className;
var clsLabel = labels[l].id.split("label-")[1];
if (($("#"+labels[l].id).css("background-color") != "rgb(247, 247, 247)")&&($("#"+labels[l].id).css("background-color") != "transparent")) {
if (cls.indexOf(clsLabel) != -1) {
rowState = 1;
}
}
}
if (rowState == 1) {
showRow(rows[r].id);
rowState = 0;
}
else {
hideRow(rows[r].id);
}
}
} else {
for (var r in rows) {
showRow(rows[r].id);
}
}
}
});
});
$("div.delete").click(function() {
var div = "#label-"+this.id;
$(div).fadeOut("slow");
});
function showRow(elem) {
if ($("#"+elem).css("display") == "none") {
$("#"+elem).fadeIn("slow");
} else {
//$("#"+elem).fadeOut(0);
}
}
$("div.div-tracker-labels").live('click', function() {
var flag = this.id;
flag = flag.replace("label-","flag-");
var bg = $("#"+flag).css("background-color");
var checkbox = $(this).find(':checkbox');
if ($(this).css("background-color") != bg) {
$(this).css("background-color",bg);
$(this).css("color","#FFFFFF");
checkbox.attr('checked', 'checked');
} else {
$(this).css("background-color","rgb(247, 247, 247)");
$(this).css("color","#565657");
checkbox.removeAttr('checked');
}
send_request('GET');
});
function hideRow(elem) {
if ($("#"+elem).css("display") != "none") {
$("#"+elem).fadeOut("fast");
} else {
//$("#"+elem).fadeOut(0);
function showRow(elem) {
if ($("#"+elem).css("display") == "none") {
$("#"+elem).fadeIn("slow");
} else {
//$("#"+elem).fadeOut(0);
}
}
function hideRow(elem) {
if ($("#"+elem).css("display") != "none") {
$("#"+elem).fadeOut("fast");
} else {
//$("#"+elem).fadeOut(0);
}
}
}
$(document).ready(function() {
$("#myradio1").live('change', function(event) {
var form = $('#filter_issues');
$.ajax({
type: "GET",
url: form.attr("action"),
data: form.serialize(),
success: function(data){
$('article').html(data);
$(".niceRadio").each(function() { changeRadioStart(jQuery(this)) });
}
});
return false;
return send_request('GET', $('#filter_issues').attr("action"));
});
$('#search_issue').live('submit', function() {
$.ajax({
type: "GET",
url: $(this).attr("action"),
data: $(this).serialize(),
success: function(data){
$('article').html(data);
$(".niceRadio").each(function() { changeRadioStart(jQuery(this)) });
}
});
return false;
return send_request('GET', $(this).attr("action"), $(this).serialize());
});
$('#add_label').live('click', function() {
return send_request('POST', $(this).attr("href"), $('#new_label').serialize());
});
$('.righter #update_label').live('click', function() {
return send_request('POST', $(this).attr("href"), $(this).parents('#update_label').serialize());
});
$('.colors .choose').live('click', function() {
var parent = $(this).parents('.colors');
parent.find('.choose.selected').removeClass('selected');
$(this).addClass('selected');
parent.siblings('.lefter').find('#label_color').val($(this).attr('value'));
});
$('.custom_color').live('click', function() {
$(this).siblings('#label_color').toggle();
return false;
});
$('article a.edit_label').live('click', function() {
$(this).parents('.label.edit').siblings('.label.edit').find('.edit_label_form').hide();
$(this).parents('.label.edit').find('.edit_label_form').toggle();
return false;
});
$('.delete_label').live('click', function() {
return send_request('POST', $(this).attr('href'));
});
function send_request(type_request, url, data) {
data = data || '';
var filter_form = $('#filter_issues');
url = url || filter_form.attr("action");
var label_form = $('#filter_labels');
var status = 'status=' + $('#issues_status').attr('value');
$.ajax({
type: type_request,
url: url,
data: filter_form.serialize() + '&' + label_form.serialize() + '&' + status + '&' + data,
success: function(data){
$('article').html(data);
$(".niceRadio").each(function() { changeRadioStart(jQuery(this)) });
},
error: function(data){
alert('error')
}
});
return false;
};
});

View File

@ -1 +1,8 @@
// PUT custom styles here ONLY
a#manage-labels {
margin-bottom: 10px;
}
article a.edit_label {
color: #FFF;
}

View File

@ -1675,7 +1675,7 @@ a.button.width100 {
width: 100px;
}
.div-tracker-lables {
.div-tracker-labels {
margin: 2px 13px 2px 0px;
cursor: pointer;
border-radius: 4px;

View File

@ -1,28 +1,37 @@
# -*- encoding : utf-8 -*-
class IssuesController < ApplicationController
before_filter :authenticate_user!
before_filter :find_project
before_filter :find_issue_by_serial_id, :only => [:show, :edit, :update, :destroy]
load_and_authorize_resource :project
load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id
load_and_authorize_resource :project, :except => [:create_lable, :delete_label]
load_and_authorize_resource :issue, :through => :project, :find_by => :serial_id, :only => [:show, :edit, :update, :destroy]
before_filter :load_and_authorize_label, :only => [:create_label, :update_label, :destroy_label]
autocomplete :user, :uname
layout 'application'
def index
@issues = @project.issues
def index(status = 200)
@is_assigned_to_me = params[:filter] == 'to_me'
@is_all = params[:filter] == 'all'
@issues = @issues.where(:user_id => current_user.id) if @is_assigned_to_me
@status = (params[:status] if ['open', 'closed'].include? params[:status]) || 'open'
@labels = params[:labels] || []
@issues = @project.issues
@issues = @issues.where(:user_id => current_user.id) if @is_assigned_to_me
@issues = @issues.joins(:labels).where(:labels => {:name => @labels}) unless @labels == []
if params[:search]
@is_assigned_to_me = false
@is_all = 'all'
@status = 'open'
@labels = []
@issues = @project.issues.where('issues.title ILIKE ?', "%#{params[:search]}%")
end
@issues = @issues.includes(:creator, :user).paginate :per_page => 10, :page => params[:page]
render :layout => request.format == '*/*' ? 'issues' : 'application' # maybe FIXME '*/*'?
@issues = @issues.includes(:creator, :user).order('serial_id desc').uniq.paginate :per_page => 10, :page => params[:page]
if status == 200
render 'index', :layout => request.format == '*/*' ? 'issues' : 'application' # maybe FIXME '*/*'?
else
render :status => status, :nothing => true
end
end
def new
@ -73,13 +82,26 @@ class IssuesController < ApplicationController
redirect_to root_path
end
def create_label
status = @project.labels.create(:name => params[:name], :color => params[:color]) ? 200 : 500
index(status)
end
def update_label
status = @label.update_attributes( :name => params[:name], :color => params[:color]) ? 200 : 500
index(status)
end
def destroy_label
status = (@label && @label_destroy) ? 200 : 500
index(status)
end
private
def find_project
def load_and_authorize_label
@project = Project.find(params[:project_id])
end
def find_issue_by_serial_id
@issue = @project.issues.find_by_serial_id!(params[:id])
@label = Label.find(params[:label_id]) if params[:label_id]
authorize! :write, @project
end
end

View File

@ -1,7 +1,9 @@
class Label < ActiveRecord::Base
has_many :labelings
has_many :labelings, :dependent => :destroy
has_many :issues, :through => :labelings
belongs_to :project
validates :name, :uniqueness => { :scope => :project_id}
validates :name, :color, :presence => true
validates :color, :format => { :with => /\A([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/, :message => I18n.t('layout.issues.invalid_labels')}
end

View File

@ -1,7 +1,5 @@
class Labeling < ActiveRecord::Base
belongs_to :issue
belongs_to :project
belongs_to :label
#before_create {|t| t.project_id = t.issue.project_id}
end

View File

@ -17,8 +17,7 @@ class Project < ActiveRecord::Base
has_many :relations, :as => :target, :dependent => :destroy
has_many :collaborators, :through => :relations, :source => :object, :source_type => 'User'
has_many :groups, :through => :relations, :source => :object, :source_type => 'Group'
has_many :labelings
has_many :labels, :through => :labelings
has_many :labels
validates :name, :uniqueness => {:scope => [:owner_id, :owner_type], :case_sensitive => false}, :presence => true, :format => { :with => /^[a-zA-Z0-9_\-\+\.]+$/ }
validates :owner, :presence => true

View File

@ -0,0 +1,6 @@
-current_color ||= '0054a6'
.colors
- ['0054a6', '00a651', 'ed1c24', 'e65c00', '9e005d', '464646', '8c6239'].each do |color|
.color{:style => "background: ##{color};"}
#choose1.choose{:value => color, :class => current_color == color ? 'selected' : ''}
.both

View File

@ -0,0 +1,47 @@
.block
%h3=t('layout.issues.labels')
#labels-stock
=form_tag project_issues_path(@project), :id => 'filter_labels', :method => :get do
- @project.labels.each_with_index do |label, index|
.div-tracker-labels{:id => "label-#{label.name}", :style => @labels.include?(label.name) ? "background-color:##{label.color};color:'#FFF'" : ''}
.div-label-left
.label
.flag{:id => "flag-#{label.name}", :style => "background-color: ##{label.color};"}
.labeltext
=label.name
=check_box_tag 'labels[]', label.name, @labels.include?(label.name), :style => 'display:none'
.both
.div-label-right=Labeling.joins(:label).where(:labels => {:name => label.name, :project_id => @project.id}).count
.both
.both
- if can? :write, @project
%a#manage-labels.button.tmargin10{:href => "#labels-stock"}=t('layout.issues.label_manage')
#labels-edit{:style => "display: none;"}
- @project.labels.each_with_index do |label, index|
.label.edit{:id => "label-#{index}"}
.labeltext.edit{:style => "background: ##{label.color};"}
.text=link_to(label.name, project_issues_update_label_path(@project, label.id), :class => 'edit_label')
.delete{:id => "delete#{index}"}
%a{:href => project_issues_delete_label_path(@project, label.id), :class => 'delete_label'}
%img{:alt => "x", :src => "/assets/x-label.png"}
.both
.edit_label_form{:style => 'display:none'}
=form_tag project_issues_update_label_path(@project, label.id), :id => 'update_label', :method => :post do
%input.gray{:name => 'name', :type => "text", :value => label.name}
=render :partial => 'issues/colors_chooser', :locals => {:current_color => label.color}
.lefter
%a{:href => "#custom_color-#{label.name}", :id => "custom_color-#{label.name}", :class => 'custom_color'}=t('layout.issues.label_custom_color')
=text_field_tag :color, label.color, :id => 'label_color', :class => 'gray', :style => 'display:none', :maxlength => 6
.righter
=link_to t('layout.update'), project_issues_update_label_path(@project, label.id), :id => 'update_label', :class => 'button'
.both
=form_tag create_label_project_issues_path(@project), :id => 'new_label', :method => :post do
%input.gray{:name => 'name', :onClick => "if(this.value=='#{t('layout.issues.new_label')}'){this.value='';this.className='black';}", :onblur => "if(this.value==''){this.value='#{t('layout.issues.new_label')}';this.className='gray';}", :type => "text", :value => "#{t('layout.issues.new_label')}"}
=render :partial => 'issues/colors_chooser'
.lefter
%a{:href => "#custom_color", :id => 'custom_color', :class => 'custom_color'}=t('layout.issues.label_custom_color')
=text_field_tag :color, '0054a6', :id => 'label_color', :class => 'gray', :style => 'display:none', :maxlength => 6
.righter
=link_to t('layout.add'), create_label_project_issues_path(@project), :id => 'add_label', :class => 'button'
.both

View File

@ -1,4 +1,4 @@
%tr#row1.2-3-stable{:name => "row"}
%tr#row1{:name => "row", :class => issue.labels.map(&:name).compact}
%td.td0
%span{:style => "display: none;"}=issue.serial_id
%td.td1=issue.serial_id
@ -9,8 +9,8 @@
=t("layout.issues.by") if issue.creator
=link_to(issue.creator.uname, user_path(issue.creator)) if issue.creator
.label.selected.tracker
.labeltext.selected{:style => "background: #39b54a;"}
2-3 stable
-issue.labels.each do |label|
.labeltext.selected{:style => "background: ##{label.color};"}=label.name
.both
%td.td3
.code=link_to '#', [@project, issue]

View File

@ -23,98 +23,4 @@
.bordered.nopadding
%h3.bmargin10=t('layout.issues.new')
= link_to t("layout.add"), new_project_issue_path(@project), :class => 'button' if can? :new, Issue.new(:project_id => @project.id)
.block
%h3 Метки
#labels-edit{:style => "display: none;"}
#label-delete1.label.edit
.labeltext.edit{:style => "background: #39b54a;"}
.text 2-3 stable
#delete1.delete
%img{:alt => "x", :src => "/assets/x-label.png"}/
.both
#label-delete2.label.edit
.labeltext.edit{:style => "background: #ed1c24;"}
.text 3-0 stable
#delete2.delete
%img{:alt => "x", :src => "/assets/x-label.png"}/
.both
#label-delete3.label.edit
.labeltext.edit{:style => "background: #ed145b;"}
.text actionpack
#delete3.delete
%img{:alt => "x", :src => "/assets/x-label.png"}/
.both
#label-delete4.label.edit
.labeltext.edit{:style => "background: #92278f;"}
.text activemodel
#delete4.delete
%img{:alt => "x", :src => "/assets/x-label.png"}/
.both
%input.gray{:onClick => "if(this.value=='Название новой метки'){this.value='';this.className='black';}", :onblur => "if(this.value==''){this.value='Название новой метки';this.className='gray';}", :type => "text", :value => "Название новой метки"}/
.colors
.color{:style => "background: #0054a6;"}
#choose1.choose.selected
.color{:style => "background: #00a651;"}
#choose2.choose
.color{:style => "background: #ed1c24;"}
#choose3.choose
.color{:style => "background: #e65c00;"}
#choose4.choose
.color{:style => "background: #9e005d;"}
#choose5.choose
.color{:style => "background: #464646;"}
#choose6.choose
.color{:style => "background: #8c6239;"}
#choose7.choose
.both
.lefter
%a{:href => "#"} Custom color
.righter
%a.button{:href => "#"} Добавить
.both
#labels-stock
#label-2-3-stable.div-tracker-lables{:name => "label"}
.div-label-left
.label
#flag-2-3-stable.flag{:style => "background-color: #39b54a;"}
.labeltext
2-3 stable
.both
.div-label-right
15
.both
.both
#label-3-0-stable.div-tracker-lables{:name => "label"}
.div-label-left
.label
#flag-3-0-stable.flag{:style => "background-color: #ed1c24;"}
.labeltext
3-0 stable
.both
.div-label-right
100
.both
.both
#label-actionpack.div-tracker-lables{:name => "label"}
.div-label-left
.label
#flag-actionpack.flag{:style => "background-color: #ed145b;"}
.labeltext
actionpack
.both
.div-label-right
22
.both
.both
#label-activemodel.div-tracker-lables{:name => "label"}
.div-label-left
.label
#flag-activemodel.flag{:style => "background-color: #92278f;"}
.labeltext
activemodel
.both
.div-label-right
3
.both
.both
%a#manage-labels.button.tmargin10{:href => "#"} Manage
=render :partial => 'labels'

View File

@ -2,6 +2,7 @@
-render :partial => 'issues/sidebar'
#closed-switcher.blue-switcher
=hidden_field_tag :issues_status, @status, :id => 'issues_status'
.open
="#{t('layout.issues.statuses.open')} (#{@issues.where(:status => 'open').count})"
#closed-tasks.closed
@ -18,5 +19,5 @@
%th{:colspan => "2"}
=t('layout.issues.description')
%tbody
- @issues.where(:status => @status).each do |issue|
- @issues.each do |issue|
= render :partial => 'issues/list', :locals => {:issue => issue}

View File

@ -23,6 +23,7 @@ en:
show: View
cancel: Cancel
create: Create
update: Update
delete: Erase
save: Save
search: Search

View File

@ -36,6 +36,7 @@ en:
labels: Labels
invalid_labels: Invalid hex color code
new_label: New label name
update_label: Update label
label_custom_color: Custom color
label_manage: Manage

View File

@ -36,6 +36,7 @@ ru:
labels: Метки
invalid_labels: Неверный hex код
new_label: Название новой метки
update_label: Обновить метку
label_custom_color: Свой цвет
label_manage: Управление

View File

@ -37,6 +37,7 @@ ru:
false_: Нет
publish: Опубликовать
add: Добавить
update: Обновить
upload: Загрузить
not_access: Нет доступа!
owner: Владелец

View File

@ -119,11 +119,11 @@ Rosa::Application.routes.draw do
resources :issues do
resources :comments, :only => [:edit, :create, :update, :destroy]
resources :subscribes, :only => [:create, :destroy]
collection do
post :create_label
post :delete_label
end
post :create_label, :on => :collection
end
post "labels/:label_id" => "issues#destroy_label", :as => :issues_delete_label
post "labels/:label_id/update" => "issues#update_label", :as => :issues_update_label
resource :repo, :controller => "git/repositories", :only => [:show]
resources :build_lists, :only => [:index, :new, :create]

View File

@ -3,6 +3,7 @@ class CreateLabels < ActiveRecord::Migration
create_table :labels do |t|
t.string :name, :null => false
t.string :color, :null => false
t.references :project
t.timestamps
end
@ -10,12 +11,11 @@ class CreateLabels < ActiveRecord::Migration
create_table :labelings do |t|
t.references :label, :null => false
t.references :issue
t.references :project
t.timestamps
end
add_index :labelings, :issue_id
add_index :labelings, :project_id
add_index :labels, :project_id
end
end

View File

@ -1,5 +0,0 @@
require 'spec_helper'
describe Tag do
pending "add some examples to (or delete) #{__FILE__}"
end