From d4a00c6321d4a9a8fd53f9462bb4e32be39beca1 Mon Sep 17 00:00:00 2001
From: Julia Miocene <jmiocene@gitlab.com>
Date: Tue, 30 Jul 2024 18:39:24 +0200
Subject: [PATCH] Update user pages empty states in admin area

Changelog: changed
---
 app/views/admin/identities/index.html.haml    | 32 ++++----
 app/views/admin/users/projects.html.haml      | 76 ++++++++++---------
 .../ssh_keys/_key_table.html.haml             | 12 +--
 .../admin/identities/index.html.haml_spec.rb  |  4 +-
 locale/gitlab.pot                             | 10 +--
 .../admin/identities/index.html.haml_spec.rb  | 16 +---
 6 files changed, 69 insertions(+), 81 deletions(-)

diff --git a/app/views/admin/identities/index.html.haml b/app/views/admin/identities/index.html.haml
index 8077f0e15cac4..db8c81de62825 100644
--- a/app/views/admin/identities/index.html.haml
+++ b/app/views/admin/identities/index.html.haml
@@ -3,21 +3,19 @@
 - page_title _("Identities"), @user.name, _("Users")
 = render 'admin/users/head'
 
-%table.table.gl-table
-  %thead
-    %tr
-      %th{ class: 'gl-border-t-0!' }= _('Provider')
-      %th{ class: 'gl-border-t-0!' }= s_('Identity|Provider ID')
-      %th{ class: 'gl-border-t-0!' }= _('Group')
-      %th{ class: 'gl-border-t-0!' }= _('Identifier')
-      %th{ class: 'gl-border-t-0!' }= s_('Identity|Active')
-      %th{ class: 'gl-border-t-0!' }= _('Actions')
-  - if identity_cells_to_render?(@identities, @user)
-    = render_if_exists partial: 'admin/identities/scim_identity', collection: scim_identities_collection(@user)
-    = render @identities
-  - else
-    %tbody
+- if identity_cells_to_render?(@identities, @user)
+  %table.table.gl-table
+    %thead
       %tr
-        %td{ colspan: '6' }
-          .text-center.my-2
-            = _('This user has no identities')
+        %th{ class: '!gl-border-t-0' }= _('Provider')
+        %th{ class: '!gl-border-t-0' }= s_('Identity|Provider ID')
+        %th{ class: '!gl-border-t-0' }= _('Group')
+        %th{ class: '!gl-border-t-0' }= _('Identifier')
+        %th{ class: '!gl-border-t-0' }= s_('Identity|Active')
+        %th{ class: '!gl-border-t-0' }= _('Actions')
+      = render_if_exists partial: 'admin/identities/scim_identity', collection: scim_identities_collection(@user)
+      = render @identities
+- else
+  = render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-user-settings-md.svg',
+    title: _('This user has no identities'),
+    empty_state_options: { data: { testid: 'identities-empty-state' } })
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index 41033b044a601..7bc2ee3d61057 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -2,49 +2,51 @@
 - add_to_breadcrumbs _("Users"), admin_users_path
 - breadcrumb_title @user.name
 - page_title _("Groups and projects"), @user.name, _("Users")
-= render 'admin/users/head'
-
-- if @user.groups.any?
-  = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }, body_options: { class: 'gl-py-0 gl-px-0'}) do |c|
-    - c.with_header do
-      = _('Groups')
-    - c.with_body do
-      %ul.hover-list
-        - @user.group_members.includes(:source).find_each do |group_member|
-          - group = group_member.group
-          %li.group_member.gl-flex.gl-items-baseline
-            %strong= link_to group.name, admin_group_path(group)
-            &ndash; access to
-            #{pluralize(group.projects.count, 'project')}
-            %span.light.gl-ml-auto= group_member.human_access
-            = render_if_exists 'admin/users/custom_role_badge', member: group_member, css_class: 'gl-ml-3'
-            - unless group_member.owner?
-              = link_button_to nil, group_group_member_path(group, group_member), data: { confirm: remove_member_message(group_member), confirm_btn_variant: 'danger', testid: 'remove-user' }, aria: { label: _('Remove') }, method: :delete, remote: true, class: 'gl-ml-3 gl-self-end', title: _('Remove user from group'), variant: :danger, size: :small, icon: 'remove'
-
-.row
-  .col-md-6
-    - if @personal_projects.present?
-      = render 'admin/users/projects', projects: @personal_projects
-    - else
-      .nothing-here-block= _('This user has no personal projects.')
+- show_empty_state = !@user.groups.any? && !@personal_projects.present? && !@joined_projects.present?
 
+= render 'admin/users/head'
 
-  .col-md-6
+- if show_empty_state
+  = render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-groups-md.svg',
+    title: _('No groups or projects found'))
+- else
+  - if @user.groups.any?
     = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }, body_options: { class: 'gl-py-0 gl-px-0'}) do |c|
       - c.with_header do
-        = _('Joined projects (%{projects_count})') % { projects_count: @joined_projects.count }
+        = _('Groups')
       - c.with_body do
         %ul.hover-list
-          - @joined_projects.sort_by(&:full_name).each do |project|
-            - member = project.team.find_member(@user.id)
-            %li.project_member.gl-flex.gl-items-baseline
-              = link_to admin_project_path(project), class: dom_class(project) do
-                = project.full_name
+          - @user.group_members.includes(:source).find_each do |group_member|
+            - group = group_member.group
+            %li.group_member.gl-flex.gl-items-baseline
+              %strong= link_to group.name, admin_group_path(group)
+              &ndash; access to
+              #{pluralize(group.projects.count, 'project')}
+              %span.light.gl-ml-auto= group_member.human_access
+              = render_if_exists 'admin/users/custom_role_badge', member: group_member, css_class: 'gl-ml-3'
+              - unless group_member.owner?
+                = link_button_to nil, group_group_member_path(group, group_member), data: { confirm: remove_member_message(group_member), confirm_btn_variant: 'danger', testid: 'remove-user' }, aria: { label: _('Remove') }, method: :delete, remote: true, class: 'gl-ml-3 gl-self-end', title: _('Remove user from group'), variant: :danger, size: :small, icon: 'remove'
+
+  .gl-grid.sm:gl-grid-cols-2.gl-gap-5
+    - if @personal_projects.present?
+      = render 'admin/users/projects', projects: @personal_projects
+    - if @joined_projects.present?
+      = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }, body_options: { class: 'gl-py-0 gl-px-0'}) do |c|
+        - c.with_header do
+          = _('Joined projects (%{projects_count})') % { projects_count: @joined_projects.count }
+        - c.with_body do
+          %ul.hover-list
+            - @joined_projects.sort_by(&:full_name).each do |project|
+              - member = project.team.find_member(@user.id)
+              %li.project_member.gl-flex.gl-items-baseline
+                .list-item-name
+                  = link_to admin_project_path(project), class: dom_class(project) do
+                    = project.full_name
 
-              - if member
-                %span.light.gl-ml-auto= member.human_access
-                = render_if_exists 'admin/users/custom_role_badge', member: member, css_class: 'gl-ml-3'
+                - if member
+                  %span.light.gl-ml-auto= member.human_access
+                  = render_if_exists 'admin/users/custom_role_badge', member: member, css_class: 'gl-ml-3'
 
-                - if member.respond_to? :project
-                  = link_button_to nil, project_project_member_path(project, member), data: { confirm: remove_member_message(member), confirm_btn_variant: 'danger' }, aria: { label: _('Remove') }, remote: true, method: :delete, class: 'gl-ml-3 gl-self-end', title: _('Remove user from project'), variant: :danger, size: :small, icon: 'remove'
+                  - if member.respond_to? :project
+                    = link_button_to nil, project_project_member_path(project, member), data: { confirm: remove_member_message(member), confirm_btn_variant: 'danger' }, aria: { label: _('Remove') }, remote: true, method: :delete, class: 'gl-ml-3', title: _('Remove user from project'), variant: :danger, size: :small, icon: 'remove'
 -# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/views/user_settings/ssh_keys/_key_table.html.haml b/app/views/user_settings/ssh_keys/_key_table.html.haml
index 77e298eed494e..f45795d7ae243 100644
--- a/app/views/user_settings/ssh_keys/_key_table.html.haml
+++ b/app/views/user_settings/ssh_keys/_key_table.html.haml
@@ -1,5 +1,4 @@
 - is_admin = local_assigns.fetch(:admin, false)
-- hide_class = local_assigns.fetch(:hide_class, false)
 
 - if @keys.any?
   .table-holder
@@ -15,8 +14,9 @@
           %th= _('Actions')
       = render partial: 'user_settings/ssh_keys/key', collection: @keys, locals: { is_admin: is_admin }
 - else
-  %p.gl-new-card-empty.gl-px-5.gl-py-4.js-toggle-content{ class: hide_class }
-    - if is_admin
-      = _('There are no SSH keys associated with this account.')
-    - else
-      = _('There are no SSH keys with access to your account.')
+  - if is_admin
+    = render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-access-token-md.svg',
+      title: _('There are no SSH keys associated with this account'))
+  - else
+    = render Pajamas::EmptyStateComponent.new(svg_path: 'illustrations/empty-state/empty-access-md.svg',
+      title: _('There are no SSH keys with access to your account'))
diff --git a/ee/spec/views/admin/identities/index.html.haml_spec.rb b/ee/spec/views/admin/identities/index.html.haml_spec.rb
index 3ca6e219b58dc..582ea7436109e 100644
--- a/ee/spec/views/admin/identities/index.html.haml_spec.rb
+++ b/ee/spec/views/admin/identities/index.html.haml_spec.rb
@@ -24,10 +24,10 @@
       assign(:identities, [])
     end
 
-    it 'shows information text' do
+    it 'shows empty state' do
       render
 
-      expect(rendered).to include('<td colspan="6">').exactly(1)
+      expect(rendered).to include('data-testid="identities-empty-state"')
       expect(rendered).to include(_('This user has no identities'))
     end
   end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 9f017c62bf5b0..5402512dce2cd 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -35006,6 +35006,9 @@ msgstr ""
 msgid "No groups found"
 msgstr ""
 
+msgid "No groups or projects found"
+msgstr ""
+
 msgid "No issues found"
 msgstr ""
 
@@ -53625,10 +53628,10 @@ msgstr ""
 msgid "There are no GPG keys with access to your account."
 msgstr ""
 
-msgid "There are no SSH keys associated with this account."
+msgid "There are no SSH keys associated with this account"
 msgstr ""
 
-msgid "There are no SSH keys with access to your account."
+msgid "There are no SSH keys with access to your account"
 msgstr ""
 
 msgid "There are no approval rules for the given `represent_as` parameter. Use a valid User/Group/Role name instead."
@@ -54695,9 +54698,6 @@ msgstr ""
 msgid "This user has no identities"
 msgstr ""
 
-msgid "This user has no personal projects."
-msgstr ""
-
 msgid "This user has previously committed to the %{name} project."
 msgstr ""
 
diff --git a/spec/views/admin/identities/index.html.haml_spec.rb b/spec/views/admin/identities/index.html.haml_spec.rb
index a4f6579f5ef1d..7cb13a16f14ae 100644
--- a/spec/views/admin/identities/index.html.haml_spec.rb
+++ b/spec/views/admin/identities/index.html.haml_spec.rb
@@ -17,22 +17,10 @@
       assign(:identities, [])
     end
 
-    it 'shows table headers' do
+    it 'shows empty state' do
       render
 
-      expect(rendered).to include('<th class="gl-border-t-0!">').exactly(6)
-      expect(rendered).to include(_('Provider'))
-      expect(rendered).to include(s_('Identity|Provider ID'))
-      expect(rendered).to include(_('Group'))
-      expect(rendered).to include(_('Identifier'))
-      expect(rendered).to include(_('Active'))
-      expect(rendered).to include(_('Actions'))
-    end
-
-    it 'shows information text' do
-      render
-
-      expect(rendered).to include('<td colspan="6">').exactly(1)
+      expect(rendered).to include('data-testid="identities-empty-state"')
       expect(rendered).to include(_('This user has no identities'))
     end
   end
-- 
GitLab