Merge branch '52712-further-ui-improvements-to-profile-overview-tab' into 'master'
Resolve "Further UI improvements to Profile "Overview" tab" Closes #52712 See merge request gitlab-org/gitlab-ce!22977
This commit is contained in:
commit
41a3fa57f0
8 changed files with 96 additions and 104 deletions
|
@ -10,6 +10,7 @@ export default class UserOverviewBlock {
|
||||||
limit: DEFAULT_LIMIT,
|
limit: DEFAULT_LIMIT,
|
||||||
...options.requestParams,
|
...options.requestParams,
|
||||||
};
|
};
|
||||||
|
this.postRenderCallback = options.postRenderCallback;
|
||||||
this.loadData();
|
this.loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,5 +44,9 @@ export default class UserOverviewBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadingEl.classList.add('hide');
|
loadingEl.classList.add('hide');
|
||||||
|
|
||||||
|
if (this.postRenderCallback) {
|
||||||
|
this.postRenderCallback.call(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ import $ from 'jquery';
|
||||||
import axios from '~/lib/utils/axios_utils';
|
import axios from '~/lib/utils/axios_utils';
|
||||||
import Activities from '~/activities';
|
import Activities from '~/activities';
|
||||||
import { localTimeAgo } from '~/lib/utils/datetime_utility';
|
import { localTimeAgo } from '~/lib/utils/datetime_utility';
|
||||||
import { __, sprintf } from '~/locale';
|
import AjaxCache from '~/lib/utils/ajax_cache';
|
||||||
|
import { __ } from '~/locale';
|
||||||
import flash from '~/flash';
|
import flash from '~/flash';
|
||||||
import ActivityCalendar from './activity_calendar';
|
import ActivityCalendar from './activity_calendar';
|
||||||
import UserOverviewBlock from './user_overview_block';
|
import UserOverviewBlock from './user_overview_block';
|
||||||
|
@ -62,23 +63,20 @@ import UserOverviewBlock from './user_overview_block';
|
||||||
* </div>
|
* </div>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CALENDAR_TEMPLATES = {
|
const CALENDAR_TEMPLATE = `
|
||||||
activity: `
|
<div class="clearfix calendar">
|
||||||
<div class="clearfix calendar">
|
<div class="js-contrib-calendar"></div>
|
||||||
<div class="js-contrib-calendar"></div>
|
<div class="calendar-hint bottom-right"></div>
|
||||||
<div class="calendar-hint bottom-right"></div>
|
</div>
|
||||||
</div>
|
`;
|
||||||
`,
|
|
||||||
overview: `
|
|
||||||
<div class="clearfix calendar">
|
|
||||||
<div class="calendar-hint"></div>
|
|
||||||
<div class="js-contrib-calendar prepend-top-20"></div>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
};
|
|
||||||
|
|
||||||
const CALENDAR_PERIOD_6_MONTHS = 6;
|
const CALENDAR_PERIOD_6_MONTHS = 6;
|
||||||
const CALENDAR_PERIOD_12_MONTHS = 12;
|
const CALENDAR_PERIOD_12_MONTHS = 12;
|
||||||
|
/* computation based on
|
||||||
|
* width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group);
|
||||||
|
* (see activity_calendar.js)
|
||||||
|
*/
|
||||||
|
const OVERVIEW_CALENDAR_BREAKPOINT = 918;
|
||||||
|
|
||||||
export default class UserTabs {
|
export default class UserTabs {
|
||||||
constructor({ defaultAction, action, parentEl }) {
|
constructor({ defaultAction, action, parentEl }) {
|
||||||
|
@ -105,6 +103,12 @@ export default class UserTabs {
|
||||||
.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
|
.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
|
||||||
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event))
|
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event))
|
||||||
.on('click', '.gl-pagination a', event => this.changeProjectsPage(event));
|
.on('click', '.gl-pagination a', event => this.changeProjectsPage(event));
|
||||||
|
|
||||||
|
window.addEventListener('resize', () => this.onResize());
|
||||||
|
}
|
||||||
|
|
||||||
|
onResize() {
|
||||||
|
this.loadActivityCalendar();
|
||||||
}
|
}
|
||||||
|
|
||||||
changeProjectsPage(e) {
|
changeProjectsPage(e) {
|
||||||
|
@ -167,8 +171,6 @@ export default class UserTabs {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadActivityCalendar('activity');
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
// eslint-disable-next-line no-new
|
||||||
new Activities('#activity');
|
new Activities('#activity');
|
||||||
|
|
||||||
|
@ -180,10 +182,10 @@ export default class UserTabs {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadActivityCalendar('overview');
|
this.loadActivityCalendar();
|
||||||
|
|
||||||
UserTabs.renderMostRecentBlocks('#js-overview .activities-block', {
|
UserTabs.renderMostRecentBlocks('#js-overview .activities-block', {
|
||||||
requestParams: { limit: 5 },
|
requestParams: { limit: 10 },
|
||||||
});
|
});
|
||||||
UserTabs.renderMostRecentBlocks('#js-overview .projects-block', {
|
UserTabs.renderMostRecentBlocks('#js-overview .projects-block', {
|
||||||
requestParams: { limit: 10, skip_pagination: true },
|
requestParams: { limit: 10, skip_pagination: true },
|
||||||
|
@ -198,52 +200,39 @@ export default class UserTabs {
|
||||||
container,
|
container,
|
||||||
url: $(`${container} .overview-content-list`).data('href'),
|
url: $(`${container} .overview-content-list`).data('href'),
|
||||||
...options,
|
...options,
|
||||||
|
postRenderCallback: () => localTimeAgo($('.js-timeago', container)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadActivityCalendar(action) {
|
loadActivityCalendar() {
|
||||||
const monthsAgo = action === 'overview' ? CALENDAR_PERIOD_6_MONTHS : CALENDAR_PERIOD_12_MONTHS;
|
|
||||||
const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar');
|
const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar');
|
||||||
const calendarPath = $calendarWrap.data('calendarPath');
|
const calendarPath = $calendarWrap.data('calendarPath');
|
||||||
|
|
||||||
|
AjaxCache.retrieve(calendarPath)
|
||||||
|
.then(data => UserTabs.renderActivityCalendar(data, $calendarWrap))
|
||||||
|
.catch(() => flash(__('There was an error loading users activity calendar.')));
|
||||||
|
}
|
||||||
|
|
||||||
|
static renderActivityCalendar(data, $calendarWrap) {
|
||||||
|
const monthsAgo = UserTabs.getVisibleCalendarPeriod($calendarWrap);
|
||||||
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
|
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
|
||||||
const utcOffset = $calendarWrap.data('utcOffset');
|
const utcOffset = $calendarWrap.data('utcOffset');
|
||||||
let utcFormatted = 'UTC';
|
const calendarHint = __('Issues, merge requests, pushes and comments.');
|
||||||
if (utcOffset !== 0) {
|
|
||||||
utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${utcOffset / 3600}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
axios
|
$calendarWrap.html(CALENDAR_TEMPLATE);
|
||||||
.get(calendarPath)
|
|
||||||
.then(({ data }) => {
|
|
||||||
$calendarWrap.html(CALENDAR_TEMPLATES[action]);
|
|
||||||
|
|
||||||
let calendarHint = '';
|
$calendarWrap.find('.calendar-hint').text(calendarHint);
|
||||||
|
|
||||||
if (action === 'activity') {
|
// eslint-disable-next-line no-new
|
||||||
calendarHint = sprintf(
|
new ActivityCalendar(
|
||||||
__(
|
'.tab-pane.active .js-contrib-calendar',
|
||||||
'Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})',
|
'.tab-pane.active .user-calendar-activities',
|
||||||
),
|
data,
|
||||||
{ utcFormatted },
|
calendarActivitiesPath,
|
||||||
);
|
utcOffset,
|
||||||
} else if (action === 'overview') {
|
0,
|
||||||
calendarHint = __('Issues, merge requests, pushes and comments.');
|
monthsAgo,
|
||||||
}
|
);
|
||||||
|
|
||||||
$calendarWrap.find('.calendar-hint').text(calendarHint);
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-new
|
|
||||||
new ActivityCalendar(
|
|
||||||
'.tab-pane.active .js-contrib-calendar',
|
|
||||||
'.tab-pane.active .user-calendar-activities',
|
|
||||||
data,
|
|
||||||
calendarActivitiesPath,
|
|
||||||
utcOffset,
|
|
||||||
0,
|
|
||||||
monthsAgo,
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(() => flash(__('There was an error loading users activity calendar.')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLoading(status) {
|
toggleLoading(status) {
|
||||||
|
@ -267,4 +256,11 @@ export default class UserTabs {
|
||||||
getCurrentAction() {
|
getCurrentAction() {
|
||||||
return this.$parentEl.find('.nav-links a.active').data('action');
|
return this.$parentEl.find('.nav-links a.active').data('action');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getVisibleCalendarPeriod($calendarWrap) {
|
||||||
|
const width = $calendarWrap.width();
|
||||||
|
return width < OVERVIEW_CALENDAR_BREAKPOINT
|
||||||
|
? CALENDAR_PERIOD_6_MONTHS
|
||||||
|
: CALENDAR_PERIOD_12_MONTHS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,30 @@
|
||||||
|
.row
|
||||||
|
.col-12
|
||||||
|
.calendar-block.prepend-top-default.append-bottom-default
|
||||||
|
.user-calendar.d-none.d-sm-block{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
|
||||||
|
%h4.center.light
|
||||||
|
= spinner nil, true
|
||||||
|
.user-calendar-activities.d-none.d-sm-block
|
||||||
.row
|
.row
|
||||||
.col-md-12.col-lg-6
|
.col-md-12.col-lg-6
|
||||||
.calendar-block
|
|
||||||
.content-block.hide-bottom-border
|
|
||||||
%h4
|
|
||||||
= s_('UserProfile|Activity')
|
|
||||||
.user-calendar.d-none.d-sm-block.text-left{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
|
|
||||||
%h4.center.light
|
|
||||||
%i.fa.fa-spinner.fa-spin
|
|
||||||
.user-calendar-activities.d-none.d-sm-block
|
|
||||||
|
|
||||||
- if can?(current_user, :read_cross_project)
|
- if can?(current_user, :read_cross_project)
|
||||||
.activities-block
|
.activities-block
|
||||||
.border-bottom.prepend-top-16
|
.prepend-top-16
|
||||||
%h5
|
.d-flex.align-items-center.border-bottom
|
||||||
= s_('UserProfile|Recent contributions')
|
%h4.flex-grow
|
||||||
|
= s_('UserProfile|Activity')
|
||||||
|
= link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
|
||||||
.overview-content-list{ data: { href: user_path } }
|
.overview-content-list{ data: { href: user_path } }
|
||||||
.center.light.loading
|
.center.light.loading
|
||||||
%i.fa.fa-spinner.fa-spin
|
= spinner nil, true
|
||||||
.prepend-top-10
|
|
||||||
= link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
|
|
||||||
|
|
||||||
.col-md-12.col-lg-6
|
.col-md-12.col-lg-6
|
||||||
.projects-block
|
.projects-block
|
||||||
.border-bottom.prepend-top-16
|
.prepend-top-16
|
||||||
%h4
|
.d-flex.align-items-center.border-bottom
|
||||||
= s_('UserProfile|Personal projects')
|
%h4.flex-grow
|
||||||
|
= s_('UserProfile|Personal projects')
|
||||||
|
= link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all"
|
||||||
.overview-content-list{ data: { href: user_projects_path } }
|
.overview-content-list{ data: { href: user_projects_path } }
|
||||||
.center.light.loading
|
.center.light.loading
|
||||||
%i.fa.fa-spinner.fa-spin
|
= spinner nil, true
|
||||||
.prepend-top-10
|
|
||||||
= link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all"
|
|
||||||
|
|
|
@ -124,12 +124,6 @@
|
||||||
|
|
||||||
- if profile_tab?(:activity)
|
- if profile_tab?(:activity)
|
||||||
#activity.tab-pane
|
#activity.tab-pane
|
||||||
.row-content-block.calendar-block.white.second-block.d-none.d-sm-block
|
|
||||||
.user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
|
|
||||||
%h4.center.light
|
|
||||||
%i.fa.fa-spinner.fa-spin
|
|
||||||
.user-calendar-activities
|
|
||||||
|
|
||||||
- if can?(current_user, :read_cross_project)
|
- if can?(current_user, :read_cross_project)
|
||||||
%h4.prepend-top-20
|
%h4.prepend-top-20
|
||||||
= s_('UserProfile|Most Recent Activity')
|
= s_('UserProfile|Most Recent Activity')
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: UI improvements to user's profile
|
||||||
|
merge_request: 22977
|
||||||
|
author:
|
||||||
|
type: other
|
|
@ -5961,9 +5961,6 @@ msgstr ""
|
||||||
msgid "Subscribed"
|
msgid "Subscribed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "Switch branch/tag"
|
msgid "Switch branch/tag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -6837,9 +6834,6 @@ msgstr ""
|
||||||
msgid "UserProfile|Personal projects"
|
msgid "UserProfile|Personal projects"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
msgid "UserProfile|Recent contributions"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
msgid "UserProfile|Report abuse"
|
msgid "UserProfile|Report abuse"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do
|
||||||
end
|
end
|
||||||
|
|
||||||
def selected_day_activities(visible: true)
|
def selected_day_activities(visible: true)
|
||||||
find('.tab-pane#activity .user-calendar-activities', visible: visible).text
|
find('#js-overview .user-calendar-activities', visible: visible).text
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
|
@ -74,16 +74,16 @@ describe 'Contributions Calendar', :js do
|
||||||
describe 'calendar day selection' do
|
describe 'calendar day selection' do
|
||||||
before do
|
before do
|
||||||
visit user.username
|
visit user.username
|
||||||
page.find('.js-activity-tab a').click
|
page.find('.js-overview-tab a').click
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays calendar' do
|
it 'displays calendar' do
|
||||||
expect(find('.tab-pane#activity')).to have_css('.js-contrib-calendar')
|
expect(find('#js-overview')).to have_css('.js-contrib-calendar')
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'select calendar day' do
|
describe 'select calendar day' do
|
||||||
let(:cells) { page.all('.tab-pane#activity .user-contrib-cell') }
|
let(:cells) { page.all('#js-overview .user-contrib-cell') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
cells[0].click
|
cells[0].click
|
||||||
|
@ -109,7 +109,7 @@ describe 'Contributions Calendar', :js do
|
||||||
describe 'deselect calendar day' do
|
describe 'deselect calendar day' do
|
||||||
before do
|
before do
|
||||||
cells[0].click
|
cells[0].click
|
||||||
page.find('.js-activity-tab a').click
|
page.find('.js-overview-tab a').click
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ describe 'Contributions Calendar', :js do
|
||||||
shared_context 'visit user page' do
|
shared_context 'visit user page' do
|
||||||
before do
|
before do
|
||||||
visit user.username
|
visit user.username
|
||||||
page.find('.js-activity-tab a').click
|
page.find('.js-overview-tab a').click
|
||||||
wait_for_requests
|
wait_for_requests
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -133,12 +133,12 @@ describe 'Contributions Calendar', :js do
|
||||||
include_context 'visit user page'
|
include_context 'visit user page'
|
||||||
|
|
||||||
it 'displays calendar activity square color for 1 contribution' do
|
it 'displays calendar activity square color for 1 contribution' do
|
||||||
expect(find('.tab-pane#activity')).to have_selector(get_cell_color_selector(contribution_count), count: 1)
|
expect(find('#js-overview')).to have_selector(get_cell_color_selector(contribution_count), count: 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays calendar activity square on the correct date' do
|
it 'displays calendar activity square on the correct date' do
|
||||||
today = Date.today.strftime(date_format)
|
today = Date.today.strftime(date_format)
|
||||||
expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1)
|
expect(find('#js-overview')).to have_selector(get_cell_date_selector(contribution_count, today), count: 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ describe 'Contributions Calendar', :js do
|
||||||
include_context 'visit user page'
|
include_context 'visit user page'
|
||||||
|
|
||||||
it 'displays calendar activity log' do
|
it 'displays calendar activity log' do
|
||||||
expect(find('.tab-pane#activity .content_list .event-target-title')).to have_content issue_title
|
expect(find('#js-overview .overview-content-list .event-target-title')).to have_content issue_title
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -185,17 +185,17 @@ describe 'Contributions Calendar', :js do
|
||||||
include_context 'visit user page'
|
include_context 'visit user page'
|
||||||
|
|
||||||
it 'displays calendar activity squares for both days' do
|
it 'displays calendar activity squares for both days' do
|
||||||
expect(find('.tab-pane#activity')).to have_selector(get_cell_color_selector(1), count: 2)
|
expect(find('#js-overview')).to have_selector(get_cell_color_selector(1), count: 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays calendar activity square for yesterday' do
|
it 'displays calendar activity square for yesterday' do
|
||||||
yesterday = Date.yesterday.strftime(date_format)
|
yesterday = Date.yesterday.strftime(date_format)
|
||||||
expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(1, yesterday), count: 1)
|
expect(find('#js-overview')).to have_selector(get_cell_date_selector(1, yesterday), count: 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'displays calendar activity square for today' do
|
it 'displays calendar activity square for today' do
|
||||||
today = Date.today.strftime(date_format)
|
today = Date.today.strftime(date_format)
|
||||||
expect(find('.tab-pane#activity')).to have_selector(get_cell_date_selector(1, today), count: 1)
|
expect(find('#js-overview')).to have_selector(get_cell_date_selector(1, today), count: 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,15 +54,15 @@ describe 'Overview tab on a user profile', :js do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'user has 10 activities' do
|
describe 'user has 11 activities' do
|
||||||
before do
|
before do
|
||||||
10.times { push_code_contribution }
|
11.times { push_code_contribution }
|
||||||
end
|
end
|
||||||
|
|
||||||
include_context 'visit overview tab'
|
include_context 'visit overview tab'
|
||||||
|
|
||||||
it 'displays 5 entries in the list of activities' do
|
it 'displays 10 entries in the list of activities' do
|
||||||
expect(find('#js-overview')).to have_selector('.event-item', count: 5)
|
expect(find('#js-overview')).to have_selector('.event-item', count: 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows a link to the activity list' do
|
it 'shows a link to the activity list' do
|
||||||
|
|
Loading…
Reference in a new issue