Merge pull request #1315 from okriuchykhin/ok_SCI_2730

Refactor Projects view - cards view [SCI-2730]
This commit is contained in:
Alex Kriuchykhin 2018-10-03 10:51:20 +02:00 committed by GitHub
commit 79b3a6cc52
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 357 additions and 201 deletions

View file

@ -3,7 +3,7 @@ ruby:
eslint:
enabled: true
config_file: .eslintrc.json
config_file: app/assets/.eslintrc.json
scss:
config_file: .scss-lint.yml

View file

@ -7,7 +7,7 @@
// - refresh project users tab after manage user modal is closed
// - refactor view handling using library, ex. backbone.js
/* global Comments CounterBadge animateSpinner */
/* global Comments CounterBadge animateSpinner initFormSubmitLinks HelperModule */
//= require comments
(function() {
@ -28,28 +28,7 @@
var projectsViewMode = 'cards';
var projectsViewFilter = $('.projects-view-filter.active').data('filter');
function initProjectsViewFilter() {
$('.projects-view-filter').click(function(event) {
event.preventDefault();
event.stopPropagation();
$('.projects-view-filter').removeClass('active');
$(this).addClass('active');
if ($(this).data('filter') === projectsViewFilter) {
return;
}
projectsViewFilter = $(this).data('filter');
});
}
function initProjectsViewModeSwitch() {
$('input[name=projects-view-mode-selector]').on('change', function() {
if ($(this).val() === projectsViewMode) {
return;
}
projectsViewMode = $(this).val();
});
}
var projectsViewSort = 'new';
/**
* Initialize the JS for new project modal to work.
@ -129,20 +108,16 @@
animateSpinner(this);
})
.on('ajax:success', function(ev2, data2) {
// Project saved, replace changed project's title
var responseHtml = $(data2.html);
var id = responseHtml.attr('data-id');
var newTitle = responseHtml.find('.panel-title');
var existingTitle = $(".panel-project[data-id='" + id + "'] .panel-title");
existingTitle.after(newTitle);
existingTitle.remove();
// Hide modal
editProjectModal.modal('hide');
HelperModule.flashAlertMsg(data2.message, 'success');
// Project saved, reload cards view
loadCardsView();
})
.on('ajax:error', function(ev2, data2) {
$(this).renderFormErrors('project', data2.responseJSON);
$(this).renderFormErrors('project', data2.responseJSON.errors);
})
.on('ajax:complete', function() {
animateSpinner(this, false);
@ -276,8 +251,6 @@
projectActionsModalBody = projectActionsModal.find('.modal-body');
projectActionsModalFooter = projectActionsModal.find('.modal-footer');
initProjectsViewFilter();
initProjectsViewModeSwitch();
initNewProjectModal();
initEditProjectModal();
initManageUsersModal();
@ -285,6 +258,31 @@
Comments.initEditComments('.panel-project .tab-content');
Comments.initDeleteComments('.panel-project .tab-content');
$('.project-card-selector').click(function() {
if (this.checked) {
$(this).closest('.panel-project').addClass('selected');
} else {
$(this).closest('.panel-project').removeClass('selected');
}
});
// init project archive/restore function
$('.panel-project .panel-heading form')
.on('ajax:beforeSend', function() {
animateSpinner($('#projects-cards-view'));
})
.on('ajax:success', function(ev, data) {
HelperModule.flashAlertMsg(data.message, 'success');
// Project saved, reload cards view
loadCardsView();
})
.on('ajax:error', function(ev, data) {
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
})
.on('ajax:complete', function() {
animateSpinner($('#projects-cards-view'), false);
});
// initialize project tab remote loading
$('.panel-project .active').removeClass('active');
$('.panel-project .panel-footer [role=tab]')
@ -324,5 +322,70 @@
});
}
init();
function loadCardsView() {
// Load HTML with projects list
var viewContainer = $('#projects-cards-view');
animateSpinner(viewContainer, true);
$.ajax({
url: $('#projects-cards-view').data('projects-url'),
type: 'GET',
dataType: 'json',
data: {
filter: projectsViewFilter,
sort: projectsViewSort
},
success: function(data) {
viewContainer.html(data.html);
initFormSubmitLinks(viewContainer);
init();
},
error: function() {
viewContainer.html('Error loading project list');
}
});
}
function initProjectsViewFilter() {
$('.projects-view-filter').click(function(event) {
event.preventDefault();
event.stopPropagation();
$('.projects-view-filter').removeClass('active');
$(this).addClass('active');
if ($(this).data('filter') === projectsViewFilter) {
return;
}
projectsViewFilter = $(this).data('filter');
if ($('#projects-cards-view').hasClass('active')) {
loadCardsView();
}
});
}
function initProjectsViewModeSwitch() {
$('input[name=projects-view-mode-selector]').on('change', function() {
if ($(this).val() === projectsViewMode) {
return;
}
projectsViewMode = $(this).val();
});
}
function initSorting() {
$('#sortMenuDropdown a').click(function(event) {
event.preventDefault();
event.stopPropagation();
if (projectsViewSort !== $(this).data('sort')) {
$('#sortMenuDropdown a').removeClass('disabled');
projectsViewSort = $(this).data('sort');
loadCardsView();
$(this).addClass('disabled');
$('#sortMenu').dropdown('toggle');
}
});
}
initProjectsViewFilter();
initProjectsViewModeSwitch();
initSorting();
loadCardsView();
}());

View file

@ -623,8 +623,6 @@ ul.double-line > li {
color: $brand-primary;
}
#projects-index,
#project-archive,
#project-show,
#experiment-archive,
#module-archive,
@ -651,9 +649,76 @@ ul.double-line > li {
}
.panel-project {
box-shadow: 0 3px 6px $color-alto;
color: $color-silver-chalice;
&:not(.selected) .panel-heading .project-card-selector,
&:not(.selected) .panel-heading .dropdown {
display: none;
}
&:not(.selected):hover .project-card-selector,
&:not(.selected):hover .dropdown {
display: block;
}
.nav .btn-link {
padding: 10px 5px;
}
&.selected {
border-color: $brand-primary;
box-shadow: 0 3px 10px $brand-primary;
}
&.archived {
&,
.panel-heading,
.panel-body,
.panel-footer-scinote {
background-color: $color-concrete;
background-image: none;
}
}
.panel-heading {
background-color: $brand-primary;
color: $color-white;
background-color: $color-white;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
color: $color-silver-chalice;
.fas {
margin-right: 10px;
}
}
.panel-title {
color: $brand-primary;
.fas {
color: $color-silver-chalice;
}
}
.panel-body {
padding: 10px 15px;
.row {
padding: 2px;
}
}
.panel-footer {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
.nav {
padding: 0 10px;
}
.nav-tabs {
border-bottom: 0;
}
}
}

View file

@ -31,6 +31,12 @@ class ProjectsController < ApplicationController
@current_team ||= current_user.teams.first
@projects = ProjectsOverviewService.new(@current_team, current_user)
.project_cards(params)
render json: {
html: render_to_string(
partial: 'projects/index/team_projects.html.erb',
locals: { projects: @projects }
)
}
end
format.html do
current_team_switch(Team.find_by_id(params[:team])) if params[:team]
@ -128,8 +134,9 @@ class ProjectsController < ApplicationController
(project_params[:archived] == 'false' &&
!can_restore_project?(@project))
return_error = true
is_archive = URI(request.referer).path == projects_archive_path ? "restore" : "archive"
flash_error = t("projects.#{is_archive}.error_flash", name: @project.name)
is_archive = project_params[:archived] == 'true' ? 'archive' : 'restore'
flash_error =
t("projects.#{is_archive}.error_flash", name: @project.name)
end
elsif !can_manage_project?(@project)
render_403 && return
@ -137,19 +144,19 @@ class ProjectsController < ApplicationController
message_renamed = nil
message_visibility = nil
if project_params.include? :name and
project_params[:name] != @project.name then
if (project_params.include? :name) &&
(project_params[:name] != @project.name)
message_renamed = t(
"activities.rename_project",
'activities.rename_project',
user: current_user.full_name,
project_old: @project.name,
project_new: project_params[:name]
)
end
if project_params.include? :visibility and
project_params[:visibility] != @project.visibility then
if (project_params.include? :visibility) &&
(project_params[:visibility] != @project.visibility)
message_visibility = t(
"activities.change_project_visibility",
'activities.change_project_visibility',
user: current_user.full_name,
project: @project.name,
visibility: project_params[:visibility] == "visible" ?
@ -159,7 +166,7 @@ class ProjectsController < ApplicationController
end
@project.last_modified_by = current_user
if @project.update(project_params)
if !return_error && @project.update(project_params)
# Add activities if needed
if message_visibility.present?
Activity.create(
@ -179,10 +186,15 @@ class ProjectsController < ApplicationController
end
flash_success = t('projects.update.success_flash', name: @project.name)
if project_params[:archived] == 'true'
flash_success = t('projects.archive.success_flash', name: @project.name)
elsif project_params[:archived] == 'false'
flash_success = t('projects.restore.success_flash', name: @project.name)
end
respond_to do |format|
format.html {
format.html do
# Redirect URL for archive view is different as for other views.
if URI(request.referer).path == projects_archive_path
if project_params[:archived] == 'false'
# The project should be restored
unless @project.archived
@project.restore(current_user)
@ -193,56 +205,45 @@ class ProjectsController < ApplicationController
user: current_user,
project: @project,
message: t(
"activities.restore_project",
'activities.restore_project',
user: current_user.full_name,
project: @project.name
)
)
flash_success = t('projects.restore.success_flash',
name: @project.name)
end
redirect_to projects_archive_path
else
elsif @project.archived
# The project should be archived
if @project.archived
@project.archive(current_user)
@project.archive(current_user)
# "Archive project" activity
Activity.create(
type_of: :archive_project,
user: current_user,
project: @project,
message: t(
"activities.archive_project",
user: current_user.full_name,
project: @project.name
)
# "Archive project" activity
Activity.create(
type_of: :archive_project,
user: current_user,
project: @project,
message: t(
'activities.archive_project',
user: current_user.full_name,
project: @project.name
)
flash_success = t('projects.archive.success_flash', name: @project.name)
end
redirect_to projects_path
)
end
redirect_to projects_path
flash[:success] = flash_success
}
format.json {
end
format.json do
render json: {
status: :ok,
html: render_to_string({
partial: "projects/index/project.html.erb",
locals: { project: @project }
})
message: flash_success
}
}
end
end
else
return_error = true
end
if return_error then
if return_error
respond_to do |format|
format.html {
format.html do
flash[:error] = flash_error
# Redirect URL for archive view is different as for other views.
if URI(request.referer).path == projects_archive_path
@ -250,11 +251,11 @@ class ProjectsController < ApplicationController
else
redirect_to projects_path
end
}
format.json {
render json: @project.errors,
status: :unprocessable_entity
}
end
format.json do
render json: { message: flash_error, errors: @project.errors },
status: :unprocessable_entity
end
end
end
end

View file

@ -2,7 +2,7 @@ class Experiment < ApplicationRecord
include ArchivableModel
include SearchableModel
belongs_to :project, inverse_of: :experiments, optional: true
belongs_to :project, inverse_of: :experiments, touch: true, optional: true
belongs_to :created_by,
foreign_key: :created_by_id,
class_name: 'User',

View file

@ -31,7 +31,7 @@ class MyModule < ApplicationRecord
foreign_key: 'restored_by_id',
class_name: 'User',
optional: true
belongs_to :experiment, inverse_of: :my_modules, optional: true
belongs_to :experiment, inverse_of: :my_modules, touch: true, optional: true
belongs_to :my_module_group, inverse_of: :my_modules, optional: true
has_many :results, inverse_of: :my_module, dependent: :destroy
has_many :my_module_tags, inverse_of: :my_module, dependent: :destroy

View file

@ -2,6 +2,7 @@ class ProjectComment < Comment
belongs_to :project,
foreign_key: :associated_id,
inverse_of: :project_comments,
touch: true,
optional: true
validates :project, presence: true

View file

@ -6,5 +6,7 @@ class UserMyModule < ApplicationRecord
foreign_key: 'assigned_by_id',
class_name: 'User',
optional: true
belongs_to :my_module, inverse_of: :user_my_modules, optional: true
belongs_to :my_module, inverse_of: :user_my_modules,
touch: true,
optional: true
end

View file

@ -10,7 +10,7 @@ class UserProject < ApplicationRecord
foreign_key: 'assigned_by_id',
class_name: 'User',
optional: true
belongs_to :project, inverse_of: :user_projects, optional: true
belongs_to :project, inverse_of: :user_projects, touch: true, optional: true
before_destroy :destroy_associations

View file

@ -8,8 +8,8 @@ class ProjectsOverviewService
def project_cards(params)
records = fetch_records
records = records.where(archived: true) if params[:archived] == 'true'
records = records.where(archived: false) if params[:archived] == 'false'
records = records.where(archived: true) if params[:filter] == 'archived'
records = records.where(archived: false) if params[:filter] == 'active'
return records unless params[:sort]
case params[:sort]
when 'new'
@ -29,8 +29,8 @@ class ProjectsOverviewService
per_page = params[:length] == '-1' ? 10 : params[:length].to_i
page = params[:start] ? (params[:start].to_i / per_page) + 1 : 1
records = fetch_dt_records
records = records.where(archived: true) if params[:archived] == 'true'
records = records.where(archived: false) if params[:archived] == 'false'
records = records.where(archived: true) if params[:filter] == 'archived'
records = records.where(archived: false) if params[:filter] == 'active'
search_value = params.dig(:search, :value)
records = search(records, search_value) if search_value.present?
sort(records, params).page(page).per(per_page)

View file

@ -6,4 +6,4 @@
</div>
<%= f.enum_btn_group :visibility, label: t("projects.index.modal_new_project.visibility"), btn_names: { hidden: t("projects.index.modal_new_project.visibility_hidden"), visible: t("projects.index.modal_new_project.visibility_visible") } %>
<% end %>
<% end %>

View file

@ -98,12 +98,18 @@
<span class="visible-xs-inline"><i class="fas fa-sort"></i></span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="sortMenu">
<ul id="sortMenuDropdown" class="dropdown-menu" aria-labelledby="sortMenu">
<% ["new", "old", "atoz", "ztoa"].each do |sort| %>
<% if @current_sort != sort %>
<li><a href="?<%= {team: @current_team.id, sort: sort}.reject{|k,v| v.to_s == "0"}.to_query %>"><%= t('projects.index.sort_' + sort) %></a></li>
<li>
<a class="<%= 'disabled' if sort == 'new' %>" href="#" data-sort="<%= sort %>">
<%= t('projects.index.sort_' + sort) %>
</a>
</li>
<% else %>
<li class="disabled"><a href="#"><%= t('projects.index.sort_' + sort) %></a></li>
<li >
<a class="disabled" href="#"><%= t('projects.index.sort_' + sort) %></a>
</li>
<% end %>
<% end %>
</ul>
@ -113,8 +119,10 @@
</form>
</div>
<%= render partial: "projects/index/team_projects",
locals: { projects: @projects_tree } %>
<div class="tab-content">
<div class="tab-pane active" id="projects-cards-view" data-projects-url="<%= projects_path %>"></div>
<div class="tab-pane" id="projects-table-view">Table view</div>
</div>
<% end %>
</div>

View file

@ -1,20 +0,0 @@
# frozen_string_literal: true
json.data do
json.array! @projects do |project|
json.set! 'id', project.id
json.set! 'archived', project.archived
json.set! 'name', project.name
json.set! 'created_at', I18n.l(project.created_at, format: :full)
json.set! 'visibility', if project.visibility == 'hidden'
'<i class="fas fa-eye-slash"></i>' +
I18n.t('projects.index.hidden')
else
'<i class="fas fa-eye"></i>' +
I18n.t('projects.index.visible')
end
json.set! 'user_count', project.user_count
json.set! 'notification_count', project.notification_count
json.set! 'comment_count', project.comment_count
end
end

View file

@ -1,48 +1,83 @@
<div id="<%= project.id %>" class="panel panel-default panel-project" data-id="<%= project.id %>"
data-project-users-tab-url="<%= url_for project_user_projects_path(project_id: project.id, format: :json) %>">
<% active = !project.archived %>
<div id="<%= project.id %>"
class="panel panel-default panel-project <%= 'archived' unless active %>"
data-id="<%= project.id %>"
data-project-users-tab-url="<%= url_for project_user_projects_path(project_id: project.id, format: :json) %>">
<div class="panel-heading">
<% if can_manage_project?(project) %>
<% if (active && (can_manage_project?(project) || can_archive_project?(project))) || (!active && can_restore_project?(project)) %>
<div class="dropdown pull-right">
<button class="btn btn-link dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<span class="caret"></span>
</button>
<% project_form = nil %>
<%= form_for project, method: :patch, format: :html do |f| %>
<%= form_for project, format: :json, method: :put, remote: true do |f| %>
<% project_form = f %>
<%= f.hidden_field :archived, value: true %>
<%= f.hidden_field :archived, value: active %>
<% end %>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenu1" style="top: 105%;">
<li class="dropdown-header"><%= t('projects.index.options_header') %></li>
<li>
<%= link_to t("projects.index.edit_option"), edit_project_path(project, format: :json), remote: true, "data-action" => "edit" %>
</li>
<% if can_archive_project?(project) %>
<li><a href="#" class="form-submit-link" data-turbolinks="false" data-submit-form="<%= project_form.options[:html][:id] %>" data-confirm-form="<%= t("projects.index.archive_confirm") %>"><%= t 'projects.index.archive_option' %></a></li>
<% if active && can_manage_project?(project) %>
<li>
<%= link_to t("projects.index.edit_option"), edit_project_path(project, format: :json), remote: true, "data-action" => "edit" %>
</li>
<% elsif active && can_archive_project?(project) %>
<li>
<a href="#"
class="form-submit-link"
data-turbolinks="false"
data-submit-form="<%= project_form.options[:html][:id] %>"
data-confirm-form="<%= t("projects.index.archive_confirm") %>"><%= t 'projects.index.archive_option' %></a>
</li>
<% else %>
<li>
<a href="#"
class="form-submit-link"
data-turbolinks="false"
data-submit-form="<%= project_form.options[:html][:id] %>"><%= t 'projects.index.restore_option' %></a>
</li>
<% end %>
</ul>
</div>
<% end %>
<h3 class="panel-title">
<% if project.hidden? then %>
<span class="fas fa-eye-slash" aria-hidden="true" title="<%=t "projects.index.visibility_private" %>"></span>
<% else %>
<span class="fas fa-eye" aria-hidden="true" title="<%=t "projects.index.visibility_public" %>"></span>
<% end %>
<% if can_read_project?(project) then %>
<div class="pull-right">
<input class="project-card-selector" type="checkbox" name="project-<%= project.id %>">
</div>
<div class="panel-title">
<span class="fas <%= active ? 'fa-arrow-alt-circle-right' : 'fa-archive' %> "></span>
<% if active %>
<%= link_to project.name, project_path(project), id: "#{project.id}-project-canvas-link" %>
<% else %>
<%= project.name %>
<% end %>
</h3>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-xs-6"><%= t('projects.index.start_date') %></div>
<div class="col-xs-6">
<%=l project.created_at, format: :full_date %>
<% if project.hidden? %>
<div class="col-xs-1">
<i class="fas fa-lg fa-eye-slash" aria-hidden="true" title="<%= t("projects.index.hidden") %>"></i>
</div>
<div class="col-xs-10">
<span><%= t("projects.index.hidden") %></span>
</div>
<% else %>
<div class="col-xs-1">
<i class="fas fa-lg fa-eye" aria-hidden="true" title="<%= t("projects.index.visible") %>"></i>
</div>
<div class="col-xs-10">
<span><%= t("projects.index.visible") %></span>
</div>
<% end %>
</div>
<div class="row">
<div class="col-xs-1"></div>
<div class="col-xs-10">
<%= t('projects.index.start_date') %>
<strong><%= l(project.created_at, format: :full_date) %></strong>
</div>
</div>
</div>
@ -51,49 +86,53 @@ data-project-users-tab-url="<%= url_for project_user_projects_path(project_id: p
<!-- Nav tabs -->
<ul class="nav nav-tabs nav-tabs-less" role="tablist" data-hook="project-footer-icons">
<% if can_read_project?(project) %>
<li role="presentation">
<a class="btn btn-link" href="<%= url_for project_project_activities_path(project_id: project.id, format: :json) %>" aria-controls="activities-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-list"></span>
</a>
</li>
<li role="presentation">
<a class="btn btn-link" href="<%= url_for project_user_projects_path(project_id: project.id, format: :json) %>" aria-controls="users-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-users"></span>
<span class="badge badge-indicator users-badge-indicator <%= 'hidden' unless project.users.count.positive? %>"
data-linked-id="<%= project.id %>">
<%= project.users.count %>
</span>
</a>
</li>
<li role="presentation">
<a class="btn btn-link" href="<%= url_for notifications_project_path(id: project.id, format: :json) %>" aria-controls="notifications-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-bell"></span>
<% if project.notifications_count(current_user).positive? %>
<span class="badge badge-indicator"><%= project.notifications_count(current_user) %></span>
<% end %>
</a>
</li>
<li role="presentation">
<a class="btn btn-link" href="<%= url_for project_project_comments_path(project_id: project.id, format: :json) %>" aria-controls="comments-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-comment"></span>
<span class="badge badge-indicator comments-badge-indicator <%= 'hidden' unless project.project_comments.count.positive? %>"
data-linked-id="<%= project.id %>">
<%= project.project_comments.count %>
</span>
</a>
</li>
<% end %>
<li role="presentation">
<a class="btn btn-link <%= 'disabled' unless active %>"
href="<%= url_for project_project_activities_path(project_id: project.id, format: :json) %>"
aria-controls="activities-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-list"></span>
</a>
</li>
<li class="pull-right" role="presentation">
<a class="btn btn-link <%= 'disabled' unless active %>"
href="<%= url_for project_project_comments_path(project_id: project.id, format: :json) %>"
aria-controls="comments-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-comment"></span>
<span class="badge badge-indicator comments-badge-indicator <%= 'hidden' unless project.comment_count.positive? %>"
data-linked-id="<%= project.id %>">
<%= project.comment_count %>
</span>
</a>
</li>
<li class="pull-right" role="presentation">
<a class="btn btn-link <%= 'disabled' unless active %>"
href="<%= url_for notifications_project_path(id: project.id, format: :json) %>"
aria-controls="notifications-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-bell"></span>
<% if project.notification_count.positive? %>
<span class="badge badge-indicator"><%= project.notification_count %></span>
<% end %>
</a>
</li>
<li class="pull-right" role="presentation">
<a class="btn btn-link <%= 'disabled' unless active %>"
href="<%= url_for project_user_projects_path(project_id: project.id, format: :json) %>"
aria-controls="users-<%= project.id %>" role="tab" data-remote="true">
<span class="fas fa-users"></span>
<span class="badge badge-indicator users-badge-indicator <%= 'hidden' unless project.user_count.positive? %>"
data-linked-id="<%= project.id %>">
<%= project.user_count %>
</span>
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content" data-hook="project-dropdown-panel">
<% if can_read_project?(project) %>
<div role="tabpanel" class="tab-pane" id="activities-<%= project.id %>"></div>
<div role="tabpanel" class="tab-pane" id="users-<%= project.id %>"></div>
<div role="tabpanel" class="tab-pane" id="notifications-<%= project.id %>"></div>
<div role="tabpanel" class="tab-pane" id="comments-<%= project.id %>"></div>
<% end %>
<div role="tabpanel" class="tab-pane" id="activities-<%= project.id %>"></div>
<div role="tabpanel" class="tab-pane" id="users-<%= project.id %>"></div>
<div role="tabpanel" class="tab-pane" id="notifications-<%= project.id %>"></div>
<div role="tabpanel" class="tab-pane" id="comments-<%= project.id %>"></div>
</div>
</div>

View file

@ -1,21 +1,18 @@
<div class="tab-content">
<div class="tab-pane active" id="projects-cards-view">
<div class="row">
<% projects.each_with_index do |project, i| %>
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
<%= render partial: "projects/index/project", locals: {project: project} %>
</div>
<% if (i+1) % 4 == 0 %>
<div class="clearfix visible-lg-block"></div>
<div class="row">
<% projects.each_with_index do |project, i| %>
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
<% cache project do %>
<%= render partial: "projects/index/project.html.erb", locals: { project: project } %>
<% end %>
<% if (i+1) % 3 == 0 %>
<div class="clearfix visible-md-block"></div>
<% end %>
<% if (i+1) % 2 == 0 %>
<div class="clearfix visible-sm-block"></div>
<% end %>
<% end %>
</div>
</div>
<div class="tab-pane" id="projects-table-view">Table view</div>
<% if (i + 1) % 4 == 0 %>
<div class="clearfix visible-lg-block"></div>
<% end %>
<% if (i + 1) % 3 == 0 %>
<div class="clearfix visible-md-block"></div>
<% end %>
<% if (i + 1) % 2 == 0 %>
<div class="clearfix visible-sm-block"></div>
<% end %>
<% end %>
</div>

View file

@ -247,7 +247,7 @@ en:
sort_old: "Oldest first"
sort_atoz: "From A to Z"
sort_ztoa: "From Z to A"
start_date: "Start date"
start_date: "Start date:"
activity_tab: "Activity"
users_tab: "Users"
notifications_tab: "Notifications"