Commit 337b2c80 authored by Martin Wortschack's avatar Martin Wortschack Committed by Phil Hughes

Resolve "Add new "Overview" tab on user profile page"

parent 14ed916c
...@@ -43,7 +43,15 @@ const initColorKey = () => ...@@ -43,7 +43,15 @@ const initColorKey = () =>
.domain([0, 3]); .domain([0, 3]);
export default class ActivityCalendar { export default class ActivityCalendar {
constructor(container, timestamps, calendarActivitiesPath, utcOffset = 0, firstDayOfWeek = 0) { constructor(
container,
activitiesContainer,
timestamps,
calendarActivitiesPath,
utcOffset = 0,
firstDayOfWeek = 0,
monthsAgo = 12,
) {
this.calendarActivitiesPath = calendarActivitiesPath; this.calendarActivitiesPath = calendarActivitiesPath;
this.clickDay = this.clickDay.bind(this); this.clickDay = this.clickDay.bind(this);
this.currentSelectedDate = ''; this.currentSelectedDate = '';
...@@ -66,6 +74,8 @@ export default class ActivityCalendar { ...@@ -66,6 +74,8 @@ export default class ActivityCalendar {
]; ];
this.months = []; this.months = [];
this.firstDayOfWeek = firstDayOfWeek; this.firstDayOfWeek = firstDayOfWeek;
this.activitiesContainer = activitiesContainer;
this.container = container;
// Loop through the timestamps to create a group of objects // Loop through the timestamps to create a group of objects
// The group of objects will be grouped based on the day of the week they are // The group of objects will be grouped based on the day of the week they are
...@@ -75,13 +85,13 @@ export default class ActivityCalendar { ...@@ -75,13 +85,13 @@ export default class ActivityCalendar {
const today = getSystemDate(utcOffset); const today = getSystemDate(utcOffset);
today.setHours(0, 0, 0, 0, 0); today.setHours(0, 0, 0, 0, 0);
const oneYearAgo = new Date(today); const timeAgo = new Date(today);
oneYearAgo.setFullYear(today.getFullYear() - 1); timeAgo.setMonth(today.getMonth() - monthsAgo);
const days = getDayDifference(oneYearAgo, today); const days = getDayDifference(timeAgo, today);
for (let i = 0; i <= days; i += 1) { for (let i = 0; i <= days; i += 1) {
const date = new Date(oneYearAgo); const date = new Date(timeAgo);
date.setDate(date.getDate() + i); date.setDate(date.getDate() + i);
const day = date.getDay(); const day = date.getDay();
...@@ -280,7 +290,7 @@ export default class ActivityCalendar { ...@@ -280,7 +290,7 @@ export default class ActivityCalendar {
this.currentSelectedDate.getDate(), this.currentSelectedDate.getDate(),
].join('-'); ].join('-');
$('.user-calendar-activities').html(LOADING_HTML); $(this.activitiesContainer).html(LOADING_HTML);
axios axios
.get(this.calendarActivitiesPath, { .get(this.calendarActivitiesPath, {
...@@ -289,11 +299,11 @@ export default class ActivityCalendar { ...@@ -289,11 +299,11 @@ export default class ActivityCalendar {
}, },
responseType: 'text', responseType: 'text',
}) })
.then(({ data }) => $('.user-calendar-activities').html(data)) .then(({ data }) => $(this.activitiesContainer).html(data))
.catch(() => flash(__('An error occurred while retrieving calendar activity'))); .catch(() => flash(__('An error occurred while retrieving calendar activity')));
} else { } else {
this.currentSelectedDate = ''; this.currentSelectedDate = '';
$('.user-calendar-activities').html(''); $(this.activitiesContainer).html('');
} }
} }
} }
import axios from '~/lib/utils/axios_utils';
export default class UserOverviewBlock {
constructor(options = {}) {
this.container = options.container;
this.url = options.url;
this.limit = options.limit || 20;
this.loadData();
}
loadData() {
const loadingEl = document.querySelector(`${this.container} .loading`);
loadingEl.classList.remove('hide');
axios
.get(this.url, {
params: {
limit: this.limit,
},
})
.then(({ data }) => this.render(data))
.catch(() => loadingEl.classList.add('hide'));
}
render(data) {
const { html, count } = data;
const contentList = document.querySelector(`${this.container} .overview-content-list`);
contentList.innerHTML += html;
const loadingEl = document.querySelector(`${this.container} .loading`);
if (count && count > 0) {
document.querySelector(`${this.container} .js-view-all`).classList.remove('hide');
} else {
document.querySelector(`${this.container} .nothing-here-block`).classList.add('text-left', 'p-0');
}
loadingEl.classList.add('hide');
}
}
...@@ -2,9 +2,10 @@ import $ from 'jquery'; ...@@ -2,9 +2,10 @@ 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 { __ } from '~/locale'; import { __, sprintf } 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';
/** /**
* UserTabs * UserTabs
...@@ -61,19 +62,28 @@ import ActivityCalendar from './activity_calendar'; ...@@ -61,19 +62,28 @@ import ActivityCalendar from './activity_calendar';
* </div> * </div>
*/ */
const CALENDAR_TEMPLATE = ` const CALENDAR_TEMPLATES = {
<div class="clearfix calendar"> activity: `
<div class="js-contrib-calendar"></div> <div class="clearfix calendar">
<div class="calendar-hint"> <div class="js-contrib-calendar"></div>
Summary of issues, merge requests, push events, and comments <div class="calendar-hint bottom-right"></div>
</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_12_MONTHS = 12;
export default class UserTabs { export default class UserTabs {
constructor({ defaultAction, action, parentEl }) { constructor({ defaultAction, action, parentEl }) {
this.loaded = {}; this.loaded = {};
this.defaultAction = defaultAction || 'activity'; this.defaultAction = defaultAction || 'overview';
this.action = action || this.defaultAction; this.action = action || this.defaultAction;
this.$parentEl = $(parentEl) || $(document); this.$parentEl = $(parentEl) || $(document);
this.windowLocation = window.location; this.windowLocation = window.location;
...@@ -124,6 +134,8 @@ export default class UserTabs { ...@@ -124,6 +134,8 @@ export default class UserTabs {
} }
if (action === 'activity') { if (action === 'activity') {
this.loadActivities(); this.loadActivities();
} else if (action === 'overview') {
this.loadOverviewTab();
} }
const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; const loadableActions = ['groups', 'contributed', 'projects', 'snippets'];
...@@ -154,7 +166,40 @@ export default class UserTabs { ...@@ -154,7 +166,40 @@ export default class UserTabs {
if (this.loaded.activity) { if (this.loaded.activity) {
return; return;
} }
const $calendarWrap = this.$parentEl.find('.user-calendar');
this.loadActivityCalendar('activity');
// eslint-disable-next-line no-new
new Activities();
this.loaded.activity = true;
}
loadOverviewTab() {
if (this.loaded.overview) {
return;
}
this.loadActivityCalendar('overview');
UserTabs.renderMostRecentBlocks('#js-overview .activities-block', 5);
UserTabs.renderMostRecentBlocks('#js-overview .projects-block', 10);
this.loaded.overview = true;
}
static renderMostRecentBlocks(container, limit) {
// eslint-disable-next-line no-new
new UserOverviewBlock({
container,
url: $(`${container} .overview-content-list`).data('href'),
limit,
});
}
loadActivityCalendar(action) {
const monthsAgo = action === 'overview' ? CALENDAR_PERIOD_6_MONTHS : CALENDAR_PERIOD_12_MONTHS;
const $calendarWrap = this.$parentEl.find('.tab-pane.active .user-calendar');
const calendarPath = $calendarWrap.data('calendarPath'); const calendarPath = $calendarWrap.data('calendarPath');
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset'); const utcOffset = $calendarWrap.data('utcOffset');
...@@ -166,17 +211,22 @@ export default class UserTabs { ...@@ -166,17 +211,22 @@ export default class UserTabs {
axios axios
.get(calendarPath) .get(calendarPath)
.then(({ data }) => { .then(({ data }) => {
$calendarWrap.html(CALENDAR_TEMPLATE); $calendarWrap.html(CALENDAR_TEMPLATES[action]);
$calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
let calendarHint = '';
if (action === 'activity') {
calendarHint = sprintf(__('Summary of issues, merge requests, push events, and comments (Timezone: %{utcFormatted})'), { utcFormatted });
} else if (action === 'overview') {
calendarHint = __('Issues, merge requests, pushes and comments.');
}
$calendarWrap.find('.calendar-hint').text(calendarHint);
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new ActivityCalendar('.js-contrib-calendar', data, calendarActivitiesPath, utcOffset); 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.'))); .catch(() => flash(__('There was an error loading users activity calendar.')));
// eslint-disable-next-line no-new
new Activities();
this.loaded.activity = true;
} }
toggleLoading(status) { toggleLoading(status) {
......
.calender-block { .calendar-block {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
border-top: 0; border-top: 0;
direction: rtl;
@media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) { @media (min-width: map-get($grid-breakpoints, sm)) and (max-width: map-get($grid-breakpoints, sm)) {
overflow-x: auto; overflow-x: auto;
...@@ -42,10 +41,13 @@ ...@@ -42,10 +41,13 @@
} }
.calendar-hint { .calendar-hint {
margin-top: -23px;
float: right;
font-size: 12px; font-size: 12px;
direction: ltr;
&.bottom-right {
direction: ltr;
margin-top: -23px;
float: right;
}
} }
.pika-single.gitlab-theme { .pika-single.gitlab-theme {
......
...@@ -29,11 +29,17 @@ class UsersController < ApplicationController ...@@ -29,11 +29,17 @@ class UsersController < ApplicationController
format.json do format.json do
load_events load_events
pager_json("events/_events", @events.count) pager_json("events/_events", @events.count, events: @events)
end end
end end
end end
def activity
respond_to do |format|
format.html { render 'show' }
end
end
def groups def groups
load_groups load_groups
...@@ -53,9 +59,7 @@ class UsersController < ApplicationController ...@@ -53,9 +59,7 @@ class UsersController < ApplicationController
respond_to do |format| respond_to do |format|
format.html { render 'show' } format.html { render 'show' }
format.json do format.json do
render json: { pager_json("shared/projects/_list", @projects.count, projects: @projects)
html: view_to_html_string("shared/projects/_list", projects: @projects)
}
end end
end end
end end
...@@ -125,6 +129,7 @@ class UsersController < ApplicationController ...@@ -125,6 +129,7 @@ class UsersController < ApplicationController
@projects = @projects =
PersonalProjectsFinder.new(user).execute(current_user) PersonalProjectsFinder.new(user).execute(current_user)
.page(params[:page]) .page(params[:page])
.per(params[:limit])
prepare_projects_for_rendering(@projects) prepare_projects_for_rendering(@projects)
end end
......
...@@ -31,7 +31,7 @@ class UserRecentEventsFinder ...@@ -31,7 +31,7 @@ class UserRecentEventsFinder
recent_events(params[:offset] || 0) recent_events(params[:offset] || 0)
.joins(:project) .joins(:project)
.with_associations .with_associations
.limit_recent(LIMIT, params[:offset]) .limit_recent(params[:limit].presence || LIMIT, params[:offset])
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -76,7 +76,7 @@ module UsersHelper ...@@ -76,7 +76,7 @@ module UsersHelper
tabs = [] tabs = []
if can?(current_user, :read_user_profile, @user) if can?(current_user, :read_user_profile, @user)
tabs += [:activity, :groups, :contributed, :projects, :snippets] tabs += [:overview, :activity, :groups, :contributed, :projects, :snippets]
end end
tabs tabs
......
.row
.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)
.activities-block
.content-block
%h5.prepend-top-10
= s_('UserProfile|Recent contributions')
.overview-content-list{ data: { href: user_path } }
.center.light.loading
%i.fa.fa-spinner.fa-spin
.prepend-top-10
= link_to s_('UserProfile|View all'), user_activity_path, class: "hide js-view-all"
.col-md-12.col-lg-6
.projects-block
.content-block
%h4
= s_('UserProfile|Personal projects')
.overview-content-list{ data: { href: user_projects_path } }
.center.light.loading
%i.fa.fa-spinner.fa-spin
.prepend-top-10
= link_to s_('UserProfile|View all'), user_projects_path, class: "hide js-view-all"
...@@ -12,22 +12,22 @@ ...@@ -12,22 +12,22 @@
.cover-block.user-cover-block.top-area .cover-block.user-cover-block.top-area
.cover-controls .cover-controls
- if @user == current_user - if @user == current_user
= link_to profile_path, class: 'btn btn-default has-tooltip', title: 'Edit profile', 'aria-label': 'Edit profile' do = link_to profile_path, class: 'btn btn-default has-tooltip', title: s_('UserProfile|Edit profile'), 'aria-label': 'Edit profile' do
= icon('pencil') = icon('pencil')
- elsif current_user - elsif current_user
- if @user.abuse_report - if @user.abuse_report
%button.btn.btn-danger{ title: 'Already reported for abuse', %button.btn.btn-danger{ title: s_('UserProfile|Already reported for abuse'),
data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } } data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } }
= icon('exclamation-circle') = icon('exclamation-circle')
- else - else
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn', = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn',
title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do title: s_('UserProfile|Report abuse'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle') = icon('exclamation-circle')
- if can?(current_user, :read_user_profile, @user) - if can?(current_user, :read_user_profile, @user)
= link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: s_('UserProfile|Subscribe'), 'aria-label': 'Subscribe' do
= icon('rss') = icon('rss')
- if current_user && current_user.admin? - if current_user && current_user.admin?
= link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area', = link_to [:admin, @user], class: 'btn btn-default', title: s_('UserProfile|View user in admin area'),
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('users') = icon('users')
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
@#{@user.username} @#{@user.username}
- if can?(current_user, :read_user_profile, @user) - if can?(current_user, :read_user_profile, @user)
%span.middle-dot-divider %span.middle-dot-divider
Member since #{@user.created_at.to_date.to_s(:long)} = s_('Member since %{date}') % { date: @user.created_at.to_date.to_s(:long) }
.cover-desc .cover-desc
- unless @user.public_email.blank? - unless @user.public_email.blank?
...@@ -91,32 +91,40 @@ ...@@ -91,32 +91,40 @@
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
%ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs
- if profile_tab?(:overview)
%li.js-overview-tab
= link_to user_path, data: { target: 'div#js-overview', action: 'overview', toggle: 'tab' } do
= s_('UserProfile|Overview')
- if profile_tab?(:activity) - if profile_tab?(:activity)
%li.js-activity-tab %li.js-activity-tab
= link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do = link_to user_activity_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
Activity = s_('UserProfile|Activity')
- if profile_tab?(:groups) - if profile_tab?(:groups)
%li.js-groups-tab %li.js-groups-tab
= link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
Groups = s_('UserProfile|Groups')
- if profile_tab?(:contributed) - if profile_tab?(:contributed)
%li.js-contributed-tab %li.js-contributed-tab
= link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
Contributed projects = s_('UserProfile|Contributed projects')
- if profile_tab?(:projects) - if profile_tab?(:projects)
%li.js-projects-tab %li.js-projects-tab
= link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
Personal projects = s_('UserProfile|Personal projects')
- if profile_tab?(:snippets) - if profile_tab?(:snippets)
%li.js-snippets-tab %li.js-snippets-tab
= link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
Snippets = s_('UserProfile|Snippets')
%div{ class: container_class } %div{ class: container_class }
.tab-content .tab-content
- if profile_tab?(:overview)
#js-overview.tab-pane
= render "users/overview"
- if profile_tab?(:activity) - if profile_tab?(:activity)
#activity.tab-pane #activity.tab-pane
.row-content-block.calender-block.white.second-block.d-none.d-sm-block .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 } } .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 %h4.center.light
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
...@@ -124,7 +132,7 @@ ...@@ -124,7 +132,7 @@
- if can?(current_user, :read_cross_project) - if can?(current_user, :read_cross_project)
%h4.prepend-top-20 %h4.prepend-top-20
Most Recent Activity = s_('UserProfile|Most Recent Activity')
.content_list{ data: { href: user_path } } .content_list{ data: { href: user_path } }
= spinner = spinner
...@@ -155,4 +163,4 @@ ...@@ -155,4 +163,4 @@
.col-12.text-center .col-12.text-center
.text-content .text-content
%h4 %h4
This user has a private profile = s_('UserProfile|This user has a private profile')
---
title: Adds new 'Overview' tab on user profile page
merge_request: 21663
author:
type: other
...@@ -45,6 +45,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d ...@@ -45,6 +45,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d
get :contributed, as: :contributed_projects get :contributed, as: :contributed_projects
get :snippets get :snippets
get :exists get :exists
get :activity
get '/', to: redirect('%{username}'), as: nil get '/', to: redirect('%{username}'), as: nil
end end
......
...@@ -3345,6 +3345,9 @@ msgstr "" ...@@ -3345,6 +3345,9 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable." msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr "" msgstr ""
msgid "Issues, merge requests, pushes and comments."
msgstr ""
msgid "Jan" msgid "Jan"
msgstr "" msgstr ""
...@@ -3709,6 +3712,9 @@ msgstr "" ...@@ -3709,6 +3712,9 @@ msgstr ""
msgid "Median" msgid "Median"
msgstr "" msgstr ""
msgid "Member since %{date}"
msgstr ""
msgid "Members" msgid "Members"
msgstr "" msgstr ""
...@@ -5753,6 +5759,9 @@ msgstr "" ...@@ -5753,6 +5759,9 @@ msgstr ""
msgid "Subscribe at project level" msgid "Subscribe at project level"
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 ""
...@@ -6581,6 +6590,51 @@ msgstr "" ...@@ -6581,6 +6590,51 @@ msgstr ""
msgid "User map" msgid "User map"
msgstr "" msgstr ""
msgid "UserProfile|Activity"
msgstr ""
msgid "UserProfile|Already reported for abuse"
msgstr ""
msgid "UserProfile|Contributed projects"
msgstr ""
msgid "UserProfile|Edit profile"
msgstr ""
msgid "UserProfile|Groups"
msgstr ""
msgid "UserProfile|Most Recent Activity"
msgstr ""
msgid "UserProfile|Overview"
msgstr ""
msgid "UserProfile|Personal projects"
msgstr ""
msgid "UserProfile|Recent contributions"
msgstr ""
msgid "UserProfile|Report abuse"
msgstr ""
msgid "UserProfile|Snippets"
msgstr ""
msgid "UserProfile|Subscribe"
msgstr ""
msgid "UserProfile|This user has a private profile"
msgstr ""
msgid "UserProfile|View all"
msgstr ""
msgid "UserProfile|View user in admin area"
msgstr ""
msgid "Users" msgid "Users"
msgstr "" msgstr ""
......
...@@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do ...@@ -64,7 +64,7 @@ describe 'Contributions Calendar', :js do
end end
def selected_day_activities(visible: true) def selected_day_activities(visible: true)