Commit 729618c50fbe44d6c4bdcbdd6d15e3eaec2067a4

Authored by Dmitriy Zaporozhets
2 parents 67b0857d 95d61a09

Merge branch 'ui-improvements' into 'master'

Ui improvements
app/assets/images/Storage-UI.PNG

737 Bytes

app/assets/images/file_bin.png

219 Bytes

app/assets/images/file_dir.png

1.61 KB

app/assets/images/file_empty.png

319 Bytes

app/assets/images/file_img.png

536 Bytes

app/assets/images/file_txt.png

463 Bytes

app/assets/images/rss_ui.png

812 Bytes

app/assets/images/submodule.png

641 Bytes

app/assets/stylesheets/generic/forms.scss
@@ -98,3 +98,7 @@ label { @@ -98,3 +98,7 @@ label {
98 z-index: 2; 98 z-index: 2;
99 } 99 }
100 } 100 }
  101 +
  102 +.fieldset-form fieldset {
  103 + margin-bottom: 20px;
  104 +}
app/assets/stylesheets/generic/typography.scss
@@ -14,6 +14,7 @@ h2.page-title { @@ -14,6 +14,7 @@ h2.page-title {
14 14
15 h3.page-title { 15 h3.page-title {
16 @include page-title; 16 @include page-title;
  17 + font-size: 22px;
17 } 18 }
18 19
19 h6 { 20 h6 {
app/assets/stylesheets/main/mixins.scss
@@ -117,11 +117,11 @@ @@ -117,11 +117,11 @@
117 } 117 }
118 118
119 @mixin page-title { 119 @mixin page-title {
120 - color: #333;  
121 - font-size: 20px; 120 + color: #555;
122 line-height: 1.5; 121 line-height: 1.5;
  122 + font-weight: normal;
123 margin-top: 0px; 123 margin-top: 0px;
124 - margin-bottom: 15px; 124 + margin-bottom: 10px;
125 } 125 }
126 126
127 @mixin str-truncated($max_width: "82%") { 127 @mixin str-truncated($max_width: "82%") {
app/assets/stylesheets/sections/admin.scss
@@ -21,12 +21,22 @@ @@ -21,12 +21,22 @@
21 } 21 }
22 22
23 .admin-filter form { 23 .admin-filter form {
24 - label { width: 110px; }  
25 - .controls { margin-left: 130px; }  
26 - .form-actions { padding-left: 130px; background: #fff }  
27 - .visibility-levels {  
28 - .controls {  
29 - margin-bottom: 9px; 24 + .select2-container {
  25 + width: 100%
  26 + }
  27 +
  28 + .controls {
  29 + margin-left: 130px;
  30 + }
  31 +
  32 + .form-actions {
  33 + padding-left: 130px;
  34 + background: #fff
  35 + }
  36 +
  37 + .visibility-levels {
  38 + .controls {
  39 + margin-bottom: 9px;
30 } 40 }
31 41
32 i { 42 i {
app/assets/stylesheets/sections/tree.scss
@@ -57,6 +57,10 @@ @@ -57,6 +57,10 @@
57 } 57 }
58 } 58 }
59 59
  60 + i {
  61 + color: $bg_primary;
  62 + }
  63 +
60 img { 64 img {
61 position: relative; 65 position: relative;
62 top:-1px; 66 top:-1px;
app/controllers/admin/projects_controller.rb
@@ -4,10 +4,8 @@ class Admin::ProjectsController < Admin::ApplicationController @@ -4,10 +4,8 @@ class Admin::ProjectsController < Admin::ApplicationController
4 before_filter :repository, only: [:show, :transfer] 4 before_filter :repository, only: [:show, :transfer]
5 5
6 def index 6 def index
7 - owner_id = params[:owner_id]  
8 - user = User.find_by(id: owner_id)  
9 -  
10 - @projects = user ? user.owned_projects : Project.all 7 + @projects = Project.all
  8 + @projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present?
11 @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? 9 @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present?
12 @projects = @projects.with_push if params[:with_push].present? 10 @projects = @projects.with_push if params[:with_push].present?
13 @projects = @projects.abandoned if params[:abandoned].present? 11 @projects = @projects.abandoned if params[:abandoned].present?
app/controllers/admin/users_controller.rb
@@ -8,7 +8,8 @@ class Admin::UsersController < Admin::ApplicationController @@ -8,7 +8,8 @@ class Admin::UsersController < Admin::ApplicationController
8 end 8 end
9 9
10 def show 10 def show
11 - @projects = user.authorized_projects 11 + @personal_projects = user.personal_projects
  12 + @joined_projects = user.projects.joined(@user)
12 end 13 end
13 14
14 def new 15 def new
app/helpers/tree_helper.rb
@@ -25,8 +25,13 @@ module TreeHelper @@ -25,8 +25,13 @@ module TreeHelper
25 # 25 #
26 # type - String type of the tree item; either 'folder' or 'file' 26 # type - String type of the tree item; either 'folder' or 'file'
27 def tree_icon(type) 27 def tree_icon(type)
28 - image = type == 'folder' ? 'file_dir.png' : 'file_txt.png'  
29 - image_tag(image, size: '16x16') 28 + icon_class = if type == 'folder'
  29 + 'icon-folder-close'
  30 + else
  31 + 'icon-file-alt'
  32 + end
  33 +
  34 + content_tag :i, nil, class: icon_class
30 end 35 end
31 36
32 def tree_hex_class(content) 37 def tree_hex_class(content)
app/views/admin/projects/index.html.haml
1 .row 1 .row
2 - .col-md-4 2 + .col-md-3
3 .admin-filter 3 .admin-filter
4 = form_tag admin_projects_path, method: :get, class: '' do 4 = form_tag admin_projects_path, method: :get, class: '' do
5 .form-group 5 .form-group
@@ -7,19 +7,21 @@ @@ -7,19 +7,21 @@
7 = text_field_tag :name, params[:name], class: "form-control" 7 = text_field_tag :name, params[:name], class: "form-control"
8 8
9 .form-group 9 .form-group
10 - = label_tag :owner_id, 'Owner:'  
11 - %div  
12 - = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large input-clamp'  
13 - .checkbox  
14 - = label_tag :with_push, 'Not empty'  
15 - = check_box_tag :with_push, 1, params[:with_push]  
16 -    
17 - %span.light Projects with push events  
18 - .checkbox  
19 - = label_tag :abandoned, 'Abandoned'  
20 - = check_box_tag :abandoned, 1, params[:abandoned]  
21 -    
22 - %span.light No activity over 6 month 10 + = label_tag :namespace_id, "Namespace"
  11 + = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large'
  12 +
  13 + .form-group
  14 + %strong Activity
  15 + .checkbox
  16 + = label_tag :with_push, 'Not empty'
  17 + = check_box_tag :with_push, 1, params[:with_push]
  18 +  
  19 + %span.light Projects with push events
  20 + .checkbox
  21 + = label_tag :abandoned, 'Abandoned'
  22 + = check_box_tag :abandoned, 1, params[:abandoned]
  23 +  
  24 + %span.light No activity over 6 month
23 25
24 %fieldset 26 %fieldset
25 %strong Visibility level: 27 %strong Visibility level:
@@ -31,12 +33,12 @@ @@ -31,12 +33,12 @@
31 %span.descr 33 %span.descr
32 = visibility_level_icon(level) 34 = visibility_level_icon(level)
33 = label 35 = label
34 - .form-actions  
35 - = hidden_field_tag :sort, params[:sort]  
36 - = submit_tag "Search", class: "btn submit btn-primary"  
37 - = link_to "Reset", admin_projects_path, class: "btn" 36 + %hr
  37 + = hidden_field_tag :sort, params[:sort]
  38 + = submit_tag "Search", class: "btn submit btn-primary"
  39 + = link_to "Reset", admin_projects_path, class: "btn btn-cancel"
38 40
39 - .col-md-8 41 + .col-md-9
40 .panel.panel-default 42 .panel.panel-default
41 .panel-heading 43 .panel-heading
42 Projects (#{@projects.total_count}) 44 Projects (#{@projects.total_count})
app/views/admin/users/_form.html.haml
1 .user_new 1 .user_new
2 - = form_for [:admin, @user], html: { class: 'form-horizontal' } do |f| 2 + = form_for [:admin, @user], html: { class: 'form-horizontal fieldset-form' } do |f|
3 -if @user.errors.any? 3 -if @user.errors.any?
4 #error_explanation 4 #error_explanation
5 .alert.alert-danger 5 .alert.alert-danger
@@ -61,17 +61,14 @@ @@ -61,17 +61,14 @@
61 .col-sm-10 You cannot remove your own admin rights 61 .col-sm-10 You cannot remove your own admin rights
62 - else 62 - else
63 .col-sm-10= f.check_box :admin 63 .col-sm-10= f.check_box :admin
64 - - unless @user.new_record? || current_user == @user  
65 - .alert.alert-danger  
66 - - if @user.blocked?  
67 - %p This user is blocked and is not able to login to GitLab  
68 - = link_to 'Unblock User', unblock_admin_user_path(@user), method: :put, class: "btn btn-small"  
69 - - else  
70 - %p Blocked users will be removed from all projects & will not be able to login to GitLab.  
71 - = link_to 'Block User', block_admin_user_path(@user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-small btn-remove"  
72 %fieldset 64 %fieldset
73 %legend Profile 65 %legend Profile
74 .form-group 66 .form-group
  67 + = f.label :avatar, class: 'control-label'
  68 + .col-sm-10
  69 + = f.file_field :avatar
  70 +
  71 + .form-group
75 = f.label :skype, class: 'control-label' 72 = f.label :skype, class: 'control-label'
76 .col-sm-10= f.text_field :skype, class: 'form-control' 73 .col-sm-10= f.text_field :skype, class: 'form-control'
77 .form-group 74 .form-group
app/views/admin/users/edit.html.haml
1 %h3.page-title 1 %h3.page-title
2 - #{@user.name} →  
3 - %i.icon-edit  
4 - Edit user 2 + Edit user: #{@user.name}
  3 +.back-link
  4 + = link_to admin_user_path(@user) do
  5 + ← Back to user page
5 %hr 6 %hr
6 = render 'form' 7 = render 'form'
app/views/admin/users/new.html.haml
1 %h3.page-title 1 %h3.page-title
2 - %i.icon-plus  
3 New user 2 New user
4 %hr 3 %hr
5 = render 'form' 4 = render 'form'
app/views/admin/users/show.html.haml
1 %h3.page-title 1 %h3.page-title
2 - %span.cgray User: 2 + User:
3 = @user.name 3 = @user.name
4 - if @user.blocked? 4 - if @user.blocked?
5 %span.cred (Blocked) 5 %span.cred (Blocked)
@@ -11,112 +11,144 @@ @@ -11,112 +11,144 @@
11 %i.icon-edit 11 %i.icon-edit
12 Edit 12 Edit
13 %hr 13 %hr
  14 +%ul.nav.nav-tabs
  15 + %li.active
  16 + %a{"data-toggle" => "tab", href: "#account"} Account
  17 + %li
  18 + %a{"data-toggle" => "tab", href: "#profile"} Profile
  19 + %li
  20 + %a{"data-toggle" => "tab", href: "#groups"} Groups
  21 + %li
  22 + %a{"data-toggle" => "tab", href: "#projects"} Projects
14 23
15 -.row  
16 - .col-md-6  
17 - .panel.panel-default  
18 - .panel-heading  
19 - Account:  
20 - .pull-right  
21 - = image_tag avatar_icon(@user.email, 32), class: "avatar s32"  
22 - %ul.well-list  
23 - %li  
24 - %span.light Name:  
25 - %strong= @user.name  
26 - %li  
27 - %span.light Username:  
28 - %strong  
29 - = @user.username  
30 - %li  
31 - %span.light Email:  
32 - %strong  
33 - = mail_to @user.email  
34 - %li  
35 - %span.light Can create groups:  
36 - %strong  
37 - = @user.can_create_group ? "Yes" : "No"  
38 - %li  
39 - %span.light Personal projects limit:  
40 - %strong  
41 - = @user.projects_limit  
42 - %li  
43 - %span.light Member since:  
44 - %strong  
45 - = @user.created_at.stamp("Nov 12, 2031")  
46 - - if @user.confirmed_at  
47 - %li  
48 - %span.light Confirmed at:  
49 - %strong  
50 - = @user.confirmed_at.stamp("Nov 12, 2031")  
51 - - else  
52 - %li  
53 - %span.light Confirmed:  
54 - %strong.cred  
55 - No 24 +.tab-content
  25 + #account.tab-pane.active
  26 + .row
  27 + .col-md-6
  28 + .panel.panel-default
  29 + .panel-heading
  30 + Account:
  31 + %ul.well-list
  32 + %li
  33 + %span.light Name:
  34 + %strong= @user.name
  35 + %li
  36 + %span.light Username:
  37 + %strong
  38 + = @user.username
  39 + %li
  40 + %span.light Email:
  41 + %strong
  42 + = mail_to @user.email
  43 + - @user.emails.each do |email|
  44 + %li
  45 + %span.light Secondary email:
  46 + %strong= email.email
56 47
57 - %li  
58 - %span.light Last sign-in at:  
59 - %strong  
60 - - if @user.last_sign_in_at  
61 - = @user.last_sign_in_at.stamp("Nov 12, 2031") 48 + %li
  49 + %span.light Can create groups:
  50 + %strong
  51 + = @user.can_create_group ? "Yes" : "No"
  52 + %li
  53 + %span.light Personal projects limit:
  54 + %strong
  55 + = @user.projects_limit
  56 + %li
  57 + %span.light Member since:
  58 + %strong
  59 + = @user.created_at.stamp("Nov 12, 2031")
  60 + - if @user.confirmed_at
  61 + %li
  62 + %span.light Confirmed at:
  63 + %strong
  64 + = @user.confirmed_at.stamp("Nov 12, 2031")
62 - else 65 - else
63 - never 66 + %li
  67 + %span.light Confirmed:
  68 + %strong.cred
  69 + No
  70 +
  71 + %li
  72 + %span.light Last sign-in at:
  73 + %strong
  74 + - if @user.last_sign_in_at
  75 + = @user.last_sign_in_at.stamp("Nov 12, 2031")
  76 + - else
  77 + never
64 78
65 - - if @user.ldap_user?  
66 - %li  
67 - %span.light LDAP uid:  
68 - %strong  
69 - = @user.extern_uid 79 + - if @user.ldap_user?
  80 + %li
  81 + %span.light LDAP uid:
  82 + %strong
  83 + = @user.extern_uid
70 84
71 - - if @user.created_by  
72 - %li  
73 - %span.light Created by:  
74 - %strong  
75 - = link_to @user.created_by.name, [:admin, @user.created_by] 85 + - if @user.created_by
  86 + %li
  87 + %span.light Created by:
  88 + %strong
  89 + = link_to @user.created_by.name, [:admin, @user.created_by]
76 90
77 - - unless @user == current_user  
78 - - if @user.blocked?  
79 - .alert.alert-info  
80 - %h4 This user is blocked  
81 - %p Blocking user has the following effects:  
82 - %ul  
83 - %li User will not be able to login  
84 - %li User will not be able to access git repositories  
85 - %li User will be removed from joined projects and groups  
86 - %li Personal projects will be left  
87 - %li Owned groups will be left  
88 - %br  
89 - = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", data: { confirm: 'Are you sure?' }  
90 - - else  
91 - .alert.alert-warning  
92 - %h4 Block this user  
93 - %p Blocking user has the following effects:  
94 - %ul  
95 - %li User will not be able to login  
96 - %li User will not be able to access git repositories  
97 - %li User will be removed from joined projects and groups  
98 - %li Personal projects will be left  
99 - %li Owned groups will be left  
100 - %br  
101 - = link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-remove" 91 + .col-md-6
  92 + - unless @user == current_user
  93 + - if @user.blocked?
  94 + .alert.alert-info
  95 + %h4 This user is blocked
  96 + %p Blocking user has the following effects:
  97 + %ul
  98 + %li User will not be able to login
  99 + %li User will not be able to access git repositories
  100 + %li User will be removed from joined projects and groups
  101 + %li Personal projects will be left
  102 + %li Owned groups will be left
  103 + %br
  104 + = link_to 'Unblock user', unblock_admin_user_path(@user), method: :put, class: "btn btn-new", data: { confirm: 'Are you sure?' }
  105 + - else
  106 + .alert.alert-warning
  107 + %h4 Block this user
  108 + %p Blocking user has the following effects:
  109 + %ul
  110 + %li User will not be able to login
  111 + %li User will not be able to access git repositories
  112 + %li User will be removed from joined projects and groups
  113 + %li Personal projects will be left
  114 + %li Owned groups will be left
  115 + %br
  116 + = link_to 'Block user', block_admin_user_path(@user), data: { confirm: 'USER WILL BE BLOCKED! Are you sure?' }, method: :put, class: "btn btn-remove"
102 117
103 - .alert.alert-danger  
104 - %h4  
105 - Remove user  
106 - %p Deleting a user has the following effects:  
107 - %ul  
108 - %li All user content like authored issues, snippets, comments will be removed  
109 - - rp = @user.personal_projects.count  
110 - - unless rp.zero?  
111 - %li #{pluralize rp, 'personal project'} will be removed and cannot be restored  
112 - - if @user.solo_owned_groups.present? 118 + .alert.alert-danger
  119 + %h4
  120 + Remove user
  121 + %p Deleting a user has the following effects:
  122 + %ul
  123 + %li All user content like authored issues, snippets, comments will be removed
  124 + - rp = @user.personal_projects.count
  125 + - unless rp.zero?
  126 + %li #{pluralize rp, 'personal project'} will be removed and cannot be restored
  127 + - if @user.solo_owned_groups.present?
  128 + %li
  129 + Next groups with all content will be removed:
  130 + %strong #{@user.solo_owned_groups.map(&:name).join(', ')}
  131 + %br
  132 + = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove"
  133 +
  134 + #profile.tab-pane
  135 + .row
  136 + .col-md-6
  137 + .panel.panel-default
  138 + .panel-heading
  139 + = @user.name
  140 + %ul.well-list
  141 + %li
  142 + = image_tag avatar_icon(@user.email, 60), class: "avatar s60"
113 %li 143 %li
114 - Next groups with all content will be removed:  
115 - %strong #{@user.solo_owned_groups.map(&:name).join(', ')}  
116 - %br  
117 - = link_to 'Remove user', [:admin, @user], data: { confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?" }, method: :delete, class: "btn btn-remove" 144 + %span.light Profile page:
  145 + %strong
  146 + = link_to user_path(@user) do
  147 + = @user.username
  148 + .col-md-6
  149 + = render 'users/profile', user: @user
118 150
119 - .col-md-6 151 + #groups.tab-pane
120 - if @user.users_groups.present? 152 - if @user.users_groups.present?
121 .panel.panel-default 153 .panel.panel-default
122 .panel-heading Groups: 154 .panel-heading Groups:
@@ -131,23 +163,42 @@ @@ -131,23 +163,42 @@
131 - unless user_group.owner? 163 - unless user_group.owner?
132 = link_to group_users_group_path(group, user_group), data: { confirm: remove_user_from_group_message(group, @user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do 164 = link_to group_users_group_path(group, user_group), data: { confirm: remove_user_from_group_message(group, @user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
133 %i.icon-remove.icon-white 165 %i.icon-remove.icon-white
  166 + - else
  167 + .nothing-here-block This user has no groups.
134 168
135 - .panel.panel-default  
136 - .panel-heading Projects (#{@projects.count})  
137 - %ul.well-list  
138 - - @projects.sort_by(&:name_with_namespace).each do |project|  
139 - - tm = project.team.find_tm(@user.id)  
140 - %li.users_project  
141 - = link_to admin_project_path(project), class: dom_class(project) do  
142 - = project.name_with_namespace 169 + #projects.tab-pane
  170 + - if @user.groups.any?
  171 + .panel.panel-default
  172 + .panel-heading Group projects
  173 + %ul.well-list
  174 + - @user.groups.each do |group|
  175 + %li
  176 + %strong= group.name
  177 + – access to
  178 + #{pluralize(group.projects.count, 'project')}
143 179
144 - - if tm  
145 - .pull-right  
146 - - if tm.owner?  
147 - %span.light Owner  
148 - - else  
149 - %span.light= tm.human_access 180 + .row
  181 + .col-md-6
  182 + = render 'users/projects', projects: @personal_projects
  183 +
  184 + .col-md-6
  185 + .panel.panel-default
  186 + .panel-heading Joined projects (#{@joined_projects.count})
  187 + %ul.well-list
  188 + - @joined_projects.sort_by(&:name_with_namespace).each do |project|
  189 + - tm = project.team.find_tm(@user.id)
  190 + %li.users_project
  191 + .list-item-name
  192 + = link_to admin_project_path(project), class: dom_class(project) do
  193 + = project.name_with_namespace
  194 +
  195 + - if tm
  196 + .pull-right
  197 + - if tm.owner?
  198 + %span.light Owner
  199 + - else
  200 + %span.light= tm.human_access
150 201
151 - - if tm.respond_to? :project  
152 - = link_to project_team_member_path(project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do  
153 - %i.icon-remove 202 + - if tm.respond_to? :project
  203 + = link_to project_team_member_path(project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do
  204 + %i.icon-remove
app/views/projects/tree/_submodule_item.html.haml
1 - tree, commit = submodule_links(submodule_item) 1 - tree, commit = submodule_links(submodule_item)
2 %tr{ class: "tree-item" } 2 %tr{ class: "tree-item" }
3 %td.tree-item-file-name 3 %td.tree-item-file-name
4 - = image_tag "submodule.png" 4 + %i.icon-archive
5 %span 5 %span
6 = link_to truncate(submodule_item.name, length: 40), tree 6 = link_to truncate(submodule_item.name, length: 40), tree
7 @ 7 @
app/views/projects/tree/_tree.html.haml
@@ -36,8 +36,7 @@ @@ -36,8 +36,7 @@
36 - if @path.present? 36 - if @path.present?
37 %tr.tree-item 37 %tr.tree-item
38 %td.tree-item-file-name 38 %td.tree-item-file-name
39 - = image_tag "file_empty.png", size: '16x16'  
40 - = link_to "..", project_tree_path(@project, up_dir_path(tree)) 39 + = link_to "..", project_tree_path(@project, up_dir_path(tree)), class: 'prepend-left-10'
41 %td 40 %td
42 %td 41 %td
43 %td 42 %td
app/views/users/_projects.html.haml
1 .panel.panel-default 1 .panel.panel-default
2 .panel-heading Personal projects 2 .panel-heading Personal projects
3 %ul.well-list 3 %ul.well-list
4 - - @projects.each do |project| 4 + - projects.each do |project|
5 %li 5 %li
6 = link_to_project project 6 = link_to_project project
app/views/users/show.html.haml
@@ -13,12 +13,14 @@ @@ -13,12 +13,14 @@
13 %br 13 %br
14 %small member since #{@user.created_at.stamp("Nov 12, 2031")} 14 %small member since #{@user.created_at.stamp("Nov 12, 2031")}
15 .clearfix 15 .clearfix
16 - %h4 Groups:  
17 - = render 'groups', groups: @groups  
18 - %hr 16 +
  17 + - if @groups.any?
  18 + %h4 Groups:
  19 + = render 'groups', groups: @groups
  20 + %hr
19 %h4 User Activity: 21 %h4 User Activity:
20 = render @events 22 = render @events
21 .col-md-4 23 .col-md-4
22 = render 'profile', user: @user 24 = render 'profile', user: @user
23 - if @projects.present? 25 - if @projects.present?
24 - = render 'projects' 26 + = render 'projects', projects: @projects