diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index 75e048cd7f8e2039a4c05d1ca2e14d6b4f6de7b5..496fa9903cc50950bc9a763169a5aaa7ce38c8cc 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -215,8 +215,9 @@
           new gl.Members();
           new UsersSelect();
           break;
-        case 'projects:project_members:index':
         case 'projects:members:show':
+          new gl.MemberExpirationDate('.js-access-expiration-date-groups');
+          new GroupsSelect();
           new gl.MemberExpirationDate();
           new gl.Members();
           new UsersSelect();
@@ -262,10 +263,6 @@
         case 'projects:artifacts:browse':
           new BuildArtifacts();
           break;
-        case 'projects:group_links:index':
-          new gl.MemberExpirationDate();
-          new GroupsSelect();
-          break;
         case 'search:show':
           new Search();
           break;
diff --git a/app/assets/javascripts/member_expiration_date.js b/app/assets/javascripts/member_expiration_date.js
index 7741cd2979334ea5402371b76e1a65d7c3ca3412..fded0881343c2c5c2a3d2f2c0ece8f8c0ec501c1 100644
--- a/app/assets/javascripts/member_expiration_date.js
+++ b/app/assets/javascripts/member_expiration_date.js
@@ -5,12 +5,16 @@
   // `js-clear-input` element, then show that element when there is a value in the
   // datepicker, and make clicking on that element clear the field.
   //
-  gl.MemberExpirationDate = function() {
+  gl.MemberExpirationDate = function(newSelector) {
     function toggleClearInput() {
       $(this).closest('.clearable-input').toggleClass('has-value', $(this).val() !== '');
     }
 
-    var inputs = $('.js-access-expiration-date');
+    var selector = '.js-access-expiration-date';
+    if (typeof newSelector !== 'undefined' && newSelector !== '') {
+      selector = newSelector;
+    }
+    var inputs = $(selector);
 
     inputs.datepicker({
       dateFormat: 'yy-mm-dd',
@@ -24,7 +28,7 @@
     inputs.next('.js-clear-input').on('click', function(event) {
       event.preventDefault();
 
-      var input = $(this).closest('.clearable-input').find('.js-access-expiration-date');
+      var input = $(this).closest('.clearable-input').find(selector);
       input.datepicker('setDate', null)
         .trigger('change');
       toggleClearInput.call(input);
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index 9eaf26a0dbf966743dcfe1998824b94771491313..4296283cd64b6653a50695b1d199ffd9c2dc0cef 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -4,10 +4,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
   before_action :authorize_admin_project_member!, only: [:update]
 
   def index
-    @group_links = project.project_group_links.all
-
-    @skip_groups = @group_links.pluck(:group_id)
-    @skip_groups << project.namespace_id unless project.personal?
+    redirect_to namespace_project_settings_members_path
   end
 
   def create
@@ -19,7 +16,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
       project.project_group_links.create(
         group: group,
         group_access: params[:link_group_access],
-        expires_at: params[:expires_at]
+        expires_at: params[:expires_at] || params[:expires_at_groups]
       )
     else
       flash[:alert] = 'Please select a group.'
diff --git a/app/controllers/projects/settings/members_controller.rb b/app/controllers/projects/settings/members_controller.rb
index ee6be33c7013daee8265262e8eeea53b0cc5b49f..766a01ba5e13e72a851b850eab94fbdfda6efbdd 100644
--- a/app/controllers/projects/settings/members_controller.rb
+++ b/app/controllers/projects/settings/members_controller.rb
@@ -2,7 +2,7 @@ module Projects
   module Settings
     class MembersController < Projects::ApplicationController
       include SortingHelper
-      
+
       def show
         @sort = params[:sort].presence || sort_value_name
         @group_links = @project.project_group_links
@@ -12,15 +12,18 @@ module Projects
 
         group = @project.group
 
+        # group links
+        @group_links = @project.project_group_links.all
+
+        @skip_groups = @group_links.pluck(:group_id)
+        @skip_groups << @project.namespace_id unless project.personal?
+
         if group
           # We need `.where.not(user_id: nil)` here otherwise when a group has an
           # invitee, it would make the following query return 0 rows since a NULL
           # user_id would be present in the subquery
           # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values
-          # FIXME: This whole logic should be moved to a finder!
-          non_null_user_ids = @project_members.where.not(user_id: nil).select(:user_id)
-          group_members = group.group_members.where.not(user_id: non_null_user_ids)
-          group_members = group_members.non_invite unless can?(current_user, :admin_group, @group)
+          group_members = MembersFinder.new(@project, @group)
         end
 
         if params[:search].present?
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b476b0bc184020eb30cd98e22a16535314419fce
--- /dev/null
+++ b/app/finders/members_finder.rb
@@ -0,0 +1,13 @@
+class MembersFinder < Projects::ApplicationController
+  def initialize(project_members, group)
+    @project_members = project_members
+    @group = group
+  end
+
+  def execute
+    non_null_user_ids = @project_members.where.not(user_id: nil).select(:user_id)
+    group_members = @group.group_members.where.not(user_id: non_null_user_ids)
+    group_members = group_members.non_invite unless can?(current_user, :admin_group, @group)
+    group_members
+  end
+end
diff --git a/app/views/projects/group_links/index.html.haml b/app/views/projects/group_links/_index.html.haml
similarity index 94%
rename from app/views/projects/group_links/index.html.haml
rename to app/views/projects/group_links/_index.html.haml
index 1b0dbbb8111aaebf8eee4dfd145e18d1957471fc..585e8299c6bee262e712763faade68283acea14d 100644
--- a/app/views/projects/group_links/index.html.haml
+++ b/app/views/projects/group_links/_index.html.haml
@@ -20,7 +20,7 @@
       .form-group
         = label_tag :expires_at, 'Access expiration date', class: 'label-light'
         .clearable-input
-          = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
+          = text_field_tag :expires_at_groups, nil, class: 'form-control js-access-expiration-date-groups', placeholder: 'Select access expiration date'
           %i.clear-icon.js-clear-input
         .help-block
           On this date, all users in the group will automatically lose access to this project.
diff --git a/app/views/projects/settings/members/show.html.haml b/app/views/projects/settings/members/show.html.haml
index 36236b97236d4386f2212274a9d76125c0be9caf..78d6293b76bde1fe6d11ef721a200077b4010417 100644
--- a/app/views/projects/settings/members/show.html.haml
+++ b/app/views/projects/settings/members/show.html.haml
@@ -1,3 +1,4 @@
 - page_title "Members"
 
-=render "projects/project_members/index"
+= render "projects/project_members/index"
+= render "projects/group_links/index"