diff --git a/app/assets/javascripts/projects/details/upload_button.vue b/app/assets/javascripts/projects/details/upload_button.vue index e1c8c66a2144aba8199332a2d5d3838968bb30c2..d19ec4bcab6cc40441a26f2b18550e9ca316f8df 100644 --- a/app/assets/javascripts/projects/details/upload_button.vue +++ b/app/assets/javascripts/projects/details/upload_button.vue @@ -36,7 +36,10 @@ export default { <span> <gl-button v-gl-modal="$options.uploadBlobModalId" + variant="link" icon="upload" + class="stat-link gl-px-0!" + button-text-classes="gl-ml-2" data-testid="upload-file-button" >{{ __('Upload File') }}</gl-button > diff --git a/app/assets/stylesheets/page_bundles/project.scss b/app/assets/stylesheets/page_bundles/project.scss index 504f14051482d5172f2642a4abb6c12dd59513e1..b3477b5d4c577714c8f7441e0e57a27bf142615b 100644 --- a/app/assets/stylesheets/page_bundles/project.scss +++ b/app/assets/stylesheets/page_bundles/project.scss @@ -134,7 +134,7 @@ .stat-text, .stat-link { - padding: $gl-btn-vert-padding 0; + padding: $gl-btn-vert-padding; background-color: transparent; font-size: $gl-font-size; line-height: $gl-btn-line-height; @@ -149,7 +149,6 @@ &:hover, &:focus { text-decoration: underline; - border-bottom: 0; } .project-stat-value { @@ -159,13 +158,6 @@ .icon { color: var(--gray-500, $gl-text-color-secondary); } - - .add-license-link { - &, - .icon { - color: var(--blue-600, $blue-600); - } - } } .btn { @@ -186,3 +178,60 @@ color: var(--gl-text-color, $gl-text-color); } } + +// FF :project_overview_reorg enabled +.project-page-indicator:not(.hidden) + .project-page-layout { + --project-overview-sidebar-width: 290px; + + @include media-breakpoint-up(lg) { + display: grid; + grid-template-columns: auto var(--project-overview-sidebar-width); + gap: 2rem; + + .project-page-layout-content, + .project-page-layout-sidebar { + min-width: 1px; + } + + .project-page-layout-sidebar { + order: 2; + overflow-x: clip; + margin-right: -$gl-padding-8; + } + + .project-page-sidebar { + position: sticky; + top: calc(#{$calc-application-header-height} + #{$gl-spacing-scale-4}); + width: calc(100% + 100px); + height: calc( + #{$calc-application-viewport-height} - #{$gl-spacing-scale-4} + ); + padding-inline: $gl-padding-4; + overflow-y: scroll; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + + .project-page-sidebar-block { + width: calc(var(--project-overview-sidebar-width) - 1px); + + &:first-of-type { + padding-top: $gl-spacing-scale-1; + } + } + + .nav { + > li { + width: 100%; + } + + .btn { + justify-content: flex-start; + + &:not(.btn-dashed) { + box-shadow: none; + } + } + } + } + } +} diff --git a/app/assets/stylesheets/page_bundles/projects.scss b/app/assets/stylesheets/page_bundles/projects.scss index d252afd0b29442c533196576c6e12010aa1bb909..1afc456a16a41f6dbf87f02ec5e6a57aca527f07 100644 --- a/app/assets/stylesheets/page_bundles/projects.scss +++ b/app/assets/stylesheets/page_bundles/projects.scss @@ -235,8 +235,7 @@ } .repository-languages-bar { - height: 8px; - margin-bottom: $gl-padding; + height: 0.5rem; background-color: var(--white, $white); border-radius: $border-radius-default; diff --git a/app/helpers/stat_anchors_helper.rb b/app/helpers/stat_anchors_helper.rb index 957985d695339719234c4a2d13c8fcf434dd249e..31f5d73d020f149eab894ee2bc4df9c685eeaff6 100644 --- a/app/helpers/stat_anchors_helper.rb +++ b/app/helpers/stat_anchors_helper.rb @@ -11,12 +11,22 @@ def stat_anchor_attrs(anchor) private + def new_button_attribute(anchor) + anchor.class_modifier || 'btn-link gl-text-blue-500!' + end + def button_attribute(anchor) anchor.class_modifier || 'btn-dashed' end def extra_classes(anchor) - if anchor.is_link + if Feature.enabled?(:project_overview_reorg) + if anchor.is_link + 'stat-link gl-px-0! gl-pb-2!' + else + "stat-link gl-px-0! gl-pb-2! #{new_button_attribute(anchor)}" + end + elsif anchor.is_link 'stat-link' else "gl-button btn #{button_attribute(anchor)}" diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb index c983d8623d2fe9981a5e9377144f30c937c30ee1..a7c8f9a469df78746429a527d30415325e522f2e 100644 --- a/app/presenters/project_presenter.rb +++ b/app/presenters/project_presenter.rb @@ -21,8 +21,16 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated AnchorData = Struct.new(:is_link, :label, :link, :class_modifier, :icon, :itemprop, :data) MAX_TOPICS_TO_SHOW = 3 - def statistic_icon(icon_name = 'plus-square-o') - sprite_icon(icon_name, css_class: 'icon gl-mr-2 gl-text-gray-500') + def statistic_default_class_list + Feature.enabled?(:project_overview_reorg) ? 'icon gl-mr-3 gl-text-gray-500' : 'icon gl-mr-2 gl-text-gray-500' + end + + def statistic_default_icon + Feature.enabled?(:project_overview_reorg) ? 'plus' : 'plus-square-o' + end + + def statistic_icon(icon_name = statistic_default_icon, class_list = statistic_default_class_list) + sprite_icon(icon_name, css_class: class_list) end def statistics_anchors(show_auto_devops_callout:) @@ -288,13 +296,19 @@ def new_file_anchor_data if can_current_user_push_to_default_branch? new_file_path = empty_repo? ? ide_edit_path(project, default_branch_or_main) : project_new_blob_path(project, default_branch_or_main) - AnchorData.new(false, statistic_icon + _('New file'), new_file_path, 'btn-dashed') + if Feature.enabled?(:project_overview_reorg) + AnchorData.new(false, statistic_icon('plus', 'gl-text-blue-500! gl-mr-3') + _('New file'), new_file_path) + else + AnchorData.new(false, statistic_icon + _('New file'), new_file_path, 'btn-dashed') + end end end def readme_anchor_data if can_current_user_push_to_default_branch? && readme_path.nil? - AnchorData.new(false, statistic_icon + _('Add README'), empty_repo? ? add_readme_ide_path : add_readme_path) + icon = Feature.enabled?(:project_overview_reorg) ? statistic_icon('plus', 'gl-text-blue-500! gl-mr-3') : statistic_icon + label = icon + _('Add README') + AnchorData.new(false, label, empty_repo? ? add_readme_ide_path : add_readme_path) elsif readme_path AnchorData.new( false, @@ -308,9 +322,11 @@ def readme_anchor_data def changelog_anchor_data if can_current_user_push_to_default_branch? && repository.changelog.blank? + icon = Feature.enabled?(:project_overview_reorg) ? statistic_icon('plus', 'gl-mr-3') : statistic_icon + label = icon + _('Add CHANGELOG') AnchorData.new( false, - statistic_icon + _('Add CHANGELOG'), + label, empty_repo? ? add_changelog_ide_path : add_changelog_path ) elsif repository.changelog.present? @@ -336,9 +352,11 @@ def license_anchor_data 'license' ) elsif can_current_user_push_to_default_branch? + icon = Feature.enabled?(:project_overview_reorg) ? statistic_icon('plus', 'gl-text-blue-500! gl-mr-3') : statistic_icon + label = icon + _('Add LICENSE') AnchorData.new( false, - content_tag(:span, statistic_icon + _('Add LICENSE'), class: 'add-license-link d-flex'), + content_tag(:span, label, class: 'add-license-link d-flex'), empty_repo? ? add_license_ide_path : add_license_path ) end @@ -346,9 +364,11 @@ def license_anchor_data def contribution_guide_anchor_data if can_current_user_push_to_default_branch? && repository.contribution_guide.blank? + icon = Feature.enabled?(:project_overview_reorg) ? statistic_icon('plus', 'gl-text-blue-500! gl-mr-3') : statistic_icon + label = icon + _('Add CONTRIBUTING') AnchorData.new( false, - statistic_icon + _('Add CONTRIBUTING'), + label, empty_repo? ? add_contribution_guide_ide_path : add_contribution_guide_path ) elsif repository.contribution_guide.present? @@ -387,7 +407,11 @@ def autodevops_anchor_data(show_auto_devops_callout: false) def kubernetes_cluster_anchor_data if can_instantiate_cluster? if clusters.empty? - AnchorData.new(false, statistic_icon + _('Add Kubernetes cluster'), project_clusters_path(project)) + if Feature.enabled?(:project_overview_reorg) + AnchorData.new(false, content_tag(:span, statistic_icon('plus', 'gl-mr-3') + _('Add Kubernetes cluster'), class: 'btn-link'), project_clusters_path(project)) + else + AnchorData.new(false, content_tag(:span, statistic_icon + _('Add Kubernetes cluster')), project_clusters_path(project)) + end else cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project) @@ -402,7 +426,7 @@ def gitlab_ci_anchor_data if cicd_missing? AnchorData.new(false, statistic_icon + _('Set up CI/CD'), project_ci_pipeline_editor_path(project)) elsif repository.gitlab_ci_yml.present? - AnchorData.new(false, statistic_icon('doc-text') + _('CI/CD configuration'), project_ci_pipeline_editor_path(project), 'btn-default') + AnchorData.new(false, statistic_icon('rocket') + _('CI/CD configuration'), project_ci_pipeline_editor_path(project), 'btn-default') end end @@ -412,7 +436,9 @@ def wiki_anchor_data if project.wiki.has_home_page? AnchorData.new(false, statistic_icon('book') + _('Wiki'), project_wiki_path, 'btn-default', nil, nil) elsif can_create_wiki? - AnchorData.new(false, statistic_icon + _('Add Wiki'), project_create_wiki_path, nil, nil, nil) + icon = Feature.enabled?(:project_overview_reorg) ? statistic_icon('plus', 'gl-mr-3') : statistic_icon + label = icon + _('Add Wiki') + AnchorData.new(false, label, project_create_wiki_path, nil, nil, nil) end end @@ -457,8 +483,11 @@ def all_clusters_empty? def integrations_anchor_data return unless can?(current_user, :admin_project, project) - label = statistic_icon('settings') + _('Configure Integrations') - AnchorData.new(false, label, project_settings_integrations_path(project), nil, nil, nil) + if Feature.enabled?(:project_overview_reorg) + AnchorData.new(false, content_tag(:span, statistic_icon('plus', 'gl-blue-500! gl-mr-3') + _('Configure Integrations'), class: 'btn-link'), project_settings_integrations_path(project), nil, nil, nil) + else + AnchorData.new(false, content_tag(:span, statistic_icon('settings') + _('Configure Integrations')), project_settings_integrations_path(project), nil, nil, nil) + end end def cicd_missing? diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml index fded6fe8abb17d97bf82da307f1c1e70d5441ff2..b958c8f8035a99bd76b3a2608774ccc5fafe09a8 100644 --- a/app/views/projects/_files.html.haml +++ b/app/views/projects/_files.html.haml @@ -8,23 +8,27 @@ - add_page_startup_api_call project_blob_path(@project, tree_join(@ref, readme_path), viewer: "rich", format: "json") #tree-holder.tree-holder.clearfix.js-per-page{ data: { blame_per_page: Gitlab::Git::BlamePagination::PAGINATION_PER_PAGE } } + - if Feature.enabled?(:project_overview_reorg) + .nav-block.gl-display-flex.gl-flex-direction-column.gl-sm-flex-direction-row.gl-align-items-stretch + = render 'projects/tree/tree_header', tree: @tree + .info-well.gl-display-none.gl-sm-display-flex.project-last-commit.gl-flex-direction-column.gl-mt-5 #js-last-commit.gl-m-auto{ data: {ref_type: @ref_type.to_s} } = gl_loading_icon(size: 'md') - if project.licensed_feature_available?(:code_owners) #js-code-owners{ data: { branch: @ref, can_view_branch_rules: can_view_branch_rules?, branch_rules_path: branch_rules_path } } - .nav-block.gl-display-flex.gl-flex-direction-column.gl-sm-flex-direction-row.gl-align-items-stretch - = render 'projects/tree/tree_header', tree: @tree + - if Feature.disabled?(:project_overview_reorg) + .nav-block.gl-display-flex.gl-flex-direction-column.gl-sm-flex-direction-row.gl-align-items-stretch + = render 'projects/tree/tree_header', tree: @tree - if project.forked? #js-fork-info{ data: vue_fork_divergence_data(project, ref) } - - if is_project_overview && has_project_shortcut_buttons + - if Feature.disabled?(:project_overview_reorg) && is_project_overview && has_project_shortcut_buttons .project-buttons.gl-mb-5.js-show-on-project-root{ data: { testid: 'project-buttons' } } = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout), project_buttons: true #js-tree-list{ data: vue_file_list_data(project, ref) } - if can_edit_tree? = render 'projects/blob/new_dir' - diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml index af48bbd262feeb142ff9d4f02aca7ad8b38fa8b3..4ade8ccf348bb24439d1dbbec62045b650d39462 100644 --- a/app/views/projects/_home_panel.html.haml +++ b/app/views/projects/_home_panel.html.haml @@ -1,9 +1,9 @@ - empty_repo = @project.empty_repo? -- show_auto_devops_callout = show_auto_devops_callout?(@project) - emails_disabled = @project.emails_disabled? +- ff_reorg_disabled = Feature.disabled?(:project_overview_reorg) -.project-home-panel.js-show-on-project-root.gl-mt-4.gl-mb-5{ class: [("empty-project" if empty_repo)] } - .gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-flex-direction-column.gl-md-flex-direction-row.gl-mb-3.gl-gap-5 +%header.project-home-panel.js-show-on-project-root.gl-mt-5{ class: [("empty-project" if empty_repo)] } + .gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-flex-direction-column.gl-md-flex-direction-row.gl-gap-5 .home-panel-title-row.gl-display-flex.gl-align-items-center %div{ class: 'avatar-container rect-avatar s64 home-panel-avatar gl-flex-shrink-0 gl-w-11 gl-h-11 gl-mr-3! float-none' } = project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64, itemprop: 'image') @@ -35,25 +35,29 @@ = render 'projects/buttons/star' = render 'projects/buttons/fork' - - if can?(current_user, :read_code, @project) - %nav.project-stats - - if @project.empty_repo? - = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors - - else - = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout) - .gl-my-3 - = render "shared/projects/topics", project: @project - .home-panel-home-desc.mt-1 - - if @project.description.present? - .home-panel-description.text-break - .home-panel-description-markdown.read-more-container{ itemprop: 'description' } - = markdown_field(@project, :description) - = render Pajamas::ButtonComponent.new(category: :tertiary, variant: :link, button_options: { class: 'js-read-more-trigger gl-lg-display-none' }) do - = _("Read more") + - if ff_reorg_disabled + - if can?(current_user, :read_code, @project) + - show_auto_devops_callout = show_auto_devops_callout?(@project) + + %nav.project-stats.gl-mt-3 + - if @project.empty_repo? + = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors + - else + = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout) + .gl-my-3 + = render "shared/projects/topics", project: @project + + .home-panel-home-desc.mt-1 + - if @project.description.present? + .home-panel-description.text-break + .home-panel-description-markdown.read-more-container{ itemprop: 'description' } + = markdown_field(@project, :description) + = render Pajamas::ButtonComponent.new(category: :tertiary, variant: :link, button_options: { class: 'js-read-more-trigger gl-lg-display-none' }) do + = _("Read more") = render_if_exists "projects/home_mirror" - - if @project.badges.present? + - if ff_reorg_disabled && @project.badges.present? .project-badges.mb-2{ data: { testid: 'project-badges-content' } } - @project.badges.each do |badge| - badge_link_url = badge.rendered_link_url(@project) diff --git a/app/views/projects/_invite_members_empty_project.html.haml b/app/views/projects/_invite_members_empty_project.html.haml index 14b0e82e021977daf76505a1441b612752f7b215..730021f345a929d6c9dc3c29eabc2af890253607 100644 --- a/app/views/projects/_invite_members_empty_project.html.haml +++ b/app/views/projects/_invite_members_empty_project.html.haml @@ -1,9 +1,20 @@ -%h4.gl-mt-0.gl-mb-3{ data: { testid: 'invite-member-section', - track_label: 'invite_members_empty_project', - track_action: 'render' } } - = s_('InviteMember|Invite your team') -%p= s_('InviteMember|Add members to this project and start collaborating with your team.') -.js-invite-members-trigger{ data: { variant: 'confirm', - classes: 'gl-mb-8 gl-w-full gl-sm-w-auto', - display_text: s_('InviteMember|Invite members'), - trigger_source: 'project_empty_page' } } +- if Feature.enabled?(:project_overview_reorg) + %p.gl-font-weight-bold.gl-text-gray-900.gl-mt-0.gl-mt-n1.gl-mb-3{ data: { testid: 'invite-member-section', + track_label: 'invite_members_empty_project', + track_action: 'render' } } + = s_('InviteMember|Invite your team') + %p.gl-mb-3= s_('InviteMember|Add members to this project and start collaborating with your team.') + .js-invite-members-trigger{ data: { variant: 'confirm', + classes: 'gl-mb-3 gl-w-full gl-sm-w-auto', + display_text: s_('InviteMember|Invite members'), + trigger_source: 'project_empty_page' } } +- else + %h4.gl-mt-0.gl-mb-3{ data: { testid: 'invite-member-section', + track_label: 'invite_members_empty_project', + track_action: 'render' } } + = s_('InviteMember|Invite your team') + %p= s_('InviteMember|Add members to this project and start collaborating with your team.') + .js-invite-members-trigger{ data: { variant: 'confirm', + classes: 'gl-mb-8 gl-w-full gl-sm-w-auto', + display_text: s_('InviteMember|Invite members'), + trigger_source: 'project_empty_page' } } diff --git a/app/views/projects/_sidebar.html.haml b/app/views/projects/_sidebar.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..565f14d01d96337aff1dac76c84eb4c5b8ae0b7a --- /dev/null +++ b/app/views/projects/_sidebar.html.haml @@ -0,0 +1,61 @@ +- has_project_shortcut_buttons = !current_user || current_user.project_shortcut_buttons +- show_auto_devops_callout = show_auto_devops_callout?(@project) + +%aside.project-page-sidebar + - if @project.description.present? || @project.badges.present? + .project-page-sidebar-block.home-panel-home-desc.gl-py-4.gl-border-b.gl-border-gray-50 + -# Project description + - if @project.description.present? + .gl-display-flex.gl-justify-content-space-between.gl-mt-1.gl-pr-2 + %p.gl-font-weight-bold.gl-text-gray-900.gl-m-0= s_('ProjectPage|Project information') + = render Pajamas::ButtonComponent.new(href: edit_project_path(@project), + category: :tertiary, + icon: 'settings', + size: :small, + button_options: { class: 'has-tooltip', title: s_('ProjectPage|Project settings'), 'aria-label' => s_('ProjectPage|Project settings') }) + .home-panel-description.text-break + .home-panel-description-markdown{ itemprop: 'description' } + = markdown_field(@project, :description) + + -# Topics + - if @project.topics.present? + .gl-mb-5 + = render "shared/projects/topics", project: @project + + -# Programming languages + - if can?(current_user, :read_code, @project) && @project.repository_languages.present? + .gl-mb-2{ class: ('gl-mb-4!' if @project.badges.present?) } + = repository_languages_bar(@project.repository_languages) + + -# Badges + - if @project.badges.present? + .project-badges.gl-mb-2{ data: { testid: 'project-badges-content' } } + - @project.badges.each do |badge| + - badge_link_url = badge.rendered_link_url(@project) + %a.gl-mr-3{ href: badge_link_url, + target: '_blank', + rel: 'noopener noreferrer', + data: { testid: 'badge-image-link', qa_link_url: badge_link_url } }> + %img.project-badge{ src: badge.rendered_image_url(@project), + 'aria-hidden': true, + alt: 'Project badge' }> + + -# Invite members + - if @project.empty_repo? + .project-page-sidebar-block.gl-py-4.gl-border-b.gl-border-gray-50 + = render "invite_members_empty_project" if can_admin_project_member?(@project) + + -# Buttons + - if can?(current_user, :read_code, @project) && !@project.empty_repo? + .project-page-sidebar-block.gl-py-4.gl-border-b.gl-border-gray-50 + %nav.project-stats + = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout) + + -# Buttons + - if has_project_shortcut_buttons + .project-page-sidebar-block.gl-py-4 + .project-buttons.gl-mb-2.js-show-on-project-root{ data: { testid: 'project-buttons' } } + - if @project.empty_repo? + = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons, project_buttons: true + - else + = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout), project_buttons: true diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml index 1409b28e735b4a8614f46d658150bdb80db7e2f7..8cad1974ffa4d0ff409f615192f004d21447d83d 100644 --- a/app/views/projects/_stat_anchor_list.html.haml +++ b/app/views/projects/_stat_anchor_list.html.haml @@ -2,8 +2,9 @@ - project_buttons = local_assigns.fetch(:project_buttons, false) - return unless anchors.any? -%ul.nav{ class: (project_buttons ? 'gl-gap-3' : 'gl-gap-5') } + +%ul.nav.gl-gap-2 - anchors.each do |anchor| %li.nav-item = link_to_if(anchor.link, anchor.label, anchor.link, stat_anchor_attrs(anchor)) do - .stat-text.d-flex.align-items-center{ class: ('btn gl-button btn-default disabled' if project_buttons) }= anchor.label + .stat-text.d-flex.align-items-center{ class: ('btn gl-button btn-default gl-px-0! disabled' if project_buttons) }= anchor.label diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index ca9900a646e4d0cc7de04625552f4b72a98cf25d..a260dd2bf286d34e4420c186c5285cbdec0c190a 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -9,68 +9,142 @@ = render "home_panel" = render "archived_notice", project: @project -= render "invite_members_empty_project" if can_admin_project_member?(@project) +- if Feature.enabled?(:project_overview_reorg) + - add_page_specific_style 'page_bundles/project' -%h4.gl-mt-0.gl-mb-3 - = _('The repository for this project is empty') + .project-page-indicator.js-show-on-project-root -- if @project.can_current_user_push_code? - %p - = _('You can get started by cloning the repository or start adding files to it with one of the following options.') + .project-page-layout + .project-page-layout-content.gl-mt-5 + .project-buttons.gl-mb-5{ data: { testid: 'quick-actions-container' } } + .project-clone-holder.d-block.d-md-none + = render "shared/mobile_clone_panel" -.project-buttons{ data: { testid: 'quick-actions-container' } } - .project-code-holder.d-block.d-md-none.gl-mt-3.gl-mr-3 - = render "shared/mobile_clone_panel" + .project-clone-holder.gl-display-none.gl-md-display-flex.gl-justify-content-end.gl-w-full.gl-mt-2 + = render "projects/buttons/code", ref: @ref - .project-code-holder.d-none.d-md-inline-block.gl-mb-3.gl-mr-3.float-left - = render "projects/buttons/code", ref: @ref - = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons, project_buttons: true + = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }, body_options: { class: 'gl-new-card-body gl-bg-gray-10 gl-p-5' }) do |c| + - c.with_body do + %h4.gl-font-lg.gl-mt-0.gl-mb-2= _('The repository for this project is empty') + - if @project.can_current_user_push_code? + %p.gl-m-0.gl-text-secondary= _('You can get started by cloning the repository or start adding files to it with one of the following options.') -- if can?(current_user, :push_code, @project) - .empty-wrapper.gl-mt-4 - %h3#repo-command-line-instructions.page-title-empty - = _('Command line instructions') + - if can?(current_user, :push_code, @project) + = render Pajamas::CardComponent.new(header_options: { class: 'gl-py-4' }) do |c| + - c.with_header do + %h5.gl-font-lg.gl-m-0= _('Command line instructions') + - c.with_body do + %p + = _('You can also upload existing files from your computer using the instructions below.') + .git-empty.js-git-empty + %h5= _('Git global setup') + %pre.gl-bg-gray-10 + :preserve + git config --global user.name "#{h git_user_name}" + git config --global user.email "#{h git_user_email}" + + %h5= _('Create a new repository') + %pre.gl-bg-gray-10 + :preserve + git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} + cd #{h @project.path} + git switch --create #{h escaped_default_branch_name} + touch README.md + git add README.md + git commit -m "add README" + - if @project.can_current_user_push_to_default_branch? + %span>< + git push --set-upstream origin #{h escaped_default_branch_name } + + %h5= _('Push an existing folder') + %pre.gl-bg-gray-10 + :preserve + cd existing_folder + git init --initial-branch=#{h escaped_default_branch_name} + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} + git add . + git commit -m "Initial commit" + - if @project.can_current_user_push_to_default_branch? + %span>< + git push --set-upstream origin #{h escaped_default_branch_name } + + %h5= _('Push an existing Git repository') + %pre.gl-bg-gray-10 + :preserve + cd existing_repo + git remote rename origin old-origin + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} + - if @project.can_current_user_push_to_default_branch? + %span>< + git push --set-upstream origin --all + git push --set-upstream origin --tags + + .project-page-layout-sidebar.js-show-on-project-root.gl-mt-5 + = render "sidebar" + +- else + = render "invite_members_empty_project" if can_admin_project_member?(@project) + + %h4.gl-mt-0.gl-mb-3 + = _('The repository for this project is empty') + + - if @project.can_current_user_push_code? %p - = _('You can also upload existing files from your computer using the instructions below.') - .git-empty.js-git-empty - %h5= _('Git global setup') - %pre.bg-light - :preserve - git config --global user.name "#{h git_user_name}" - git config --global user.email "#{h git_user_email}" - - %h5= _('Create a new repository') - %pre.bg-light - :preserve - git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} - cd #{h @project.path} - git switch --create #{h escaped_default_branch_name} - touch README.md - git add README.md - git commit -m "add README" - - if @project.can_current_user_push_to_default_branch? - %span>< - git push --set-upstream origin #{h escaped_default_branch_name } - - %h5= _('Push an existing folder') - %pre.bg-light - :preserve - cd existing_folder - git init --initial-branch=#{h escaped_default_branch_name} - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} - git add . - git commit -m "Initial commit" - - if @project.can_current_user_push_to_default_branch? - %span>< - git push --set-upstream origin #{h escaped_default_branch_name } - - %h5= _('Push an existing Git repository') - %pre.bg-light - :preserve - cd existing_repo - git remote rename origin old-origin - git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} - - if @project.can_current_user_push_to_default_branch? - %span>< - git push --set-upstream origin --all - git push --set-upstream origin --tags + = _('You can get started by cloning the repository or start adding files to it with one of the following options.') + + .project-buttons{ data: { testid: 'quick-actions-container' } } + .project-clone-holder.d-block.d-md-none.gl-mt-3.gl-mr-3 + = render "shared/mobile_clone_panel" + + .project-clone-holder.d-none.d-md-inline-block.gl-mb-3.gl-mr-3.float-left + = render "projects/buttons/code", ref: @ref + = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons, project_buttons: true + + - if can?(current_user, :push_code, @project) + .empty-wrapper.gl-mt-4 + %h3#repo-command-line-instructions.page-title-empty + = _('Command line instructions') + %p + = _('You can also upload existing files from your computer using the instructions below.') + .git-empty.js-git-empty + %h5= _('Git global setup') + %pre.bg-light + :preserve + git config --global user.name "#{h git_user_name}" + git config --global user.email "#{h git_user_email}" + + %h5= _('Create a new repository') + %pre.bg-light + :preserve + git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} + cd #{h @project.path} + git switch --create #{h escaped_default_branch_name} + touch README.md + git add README.md + git commit -m "add README" + - if @project.can_current_user_push_to_default_branch? + %span>< + git push --set-upstream origin #{h escaped_default_branch_name } + + %h5= _('Push an existing folder') + %pre.bg-light + :preserve + cd existing_folder + git init --initial-branch=#{h escaped_default_branch_name} + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} + git add . + git commit -m "Initial commit" + - if @project.can_current_user_push_to_default_branch? + %span>< + git push --set-upstream origin #{h escaped_default_branch_name } + + %h5= _('Push an existing Git repository') + %pre.bg-light + :preserve + cd existing_repo + git remote rename origin old-origin + git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')} + - if @project.can_current_user_push_to_default_branch? + %span>< + git push --set-upstream origin --all + git push --set-upstream origin --tags diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index c76fa5e2220ab2774501cadb0c96bdc5d981d870..9c0badf40904b7b1c1e2a12222731320b06b46c9 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -15,15 +15,35 @@ = render "home_panel" -- if can?(current_user, :read_code, @project) && @project.repository_languages.present? - - add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, path: current_route_path || "" }) - = repository_languages_bar(@project.repository_languages) +- if Feature.enabled?(:project_overview_reorg) + .project-page-indicator.js-show-on-project-root -= render "archived_notice", project: @project -= render_if_exists "projects/marked_for_deletion_notice", project: @project -= render_if_exists "projects/ancestor_group_marked_for_deletion_notice", project: @project + .project-page-layout + .project-page-layout-sidebar.js-show-on-project-root.gl-mt-5 + = render "sidebar" -- view_path = @project.default_view + .project-page-layout-content.gl-mt-5 + - if can?(current_user, :read_code, @project) && @project.repository_languages.present? + - add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, path: current_route_path || "" }) -%div{ class: project_child_container_class(view_path) } - = render view_path, is_project_overview: true + = render "archived_notice", project: @project + = render_if_exists "projects/marked_for_deletion_notice", project: @project + = render_if_exists "projects/ancestor_group_marked_for_deletion_notice", project: @project + + - view_path = @project.default_view + + %div{ class: project_child_container_class(view_path) } + = render view_path, is_project_overview: true +- else + - if can?(current_user, :read_code, @project) && @project.repository_languages.present? + - add_page_startup_graphql_call('repository/path_last_commit', { projectPath: @project.full_path, ref: current_ref, path: current_route_path || "" }) + = repository_languages_bar(@project.repository_languages) + + = render "archived_notice", project: @project + = render_if_exists "projects/marked_for_deletion_notice", project: @project + = render_if_exists "projects/ancestor_group_marked_for_deletion_notice", project: @project + + - view_path = @project.default_view + + %div{ class: project_child_container_class(view_path) } + = render view_path, is_project_overview: true diff --git a/app/views/shared/projects/_topics.html.haml b/app/views/shared/projects/_topics.html.haml index c4524125a21e487706c4ef45a393f6afdb56f70b..33e4ac58fa5a3eb60b06d707514e5b7bea5f57ea 100644 --- a/app/views/shared/projects/_topics.html.haml +++ b/app/views/shared/projects/_topics.html.haml @@ -2,8 +2,9 @@ - if project.topics.present? .gl-w-full.gl-display-inline-flex.gl-flex-wrap.gl-font-base.gl-font-weight-normal.gl-align-items-center.gl-mx-n2.gl-my-n2{ 'data-testid': 'project_topic_list' } - %span.gl-p-2.gl-text-gray-500 - = _('Topics') + ':' + - if Feature.disabled?(:project_overview_reorg) + %span.gl-p-2.gl-text-gray-500 + = _('Topics') + ':' - project.topics_to_show.each do |topic| - explore_project_topic_path = topic_explore_projects_cleaned_path(topic_name: topic[:name]) - if topic[:title].length > max_project_topic_length diff --git a/config/feature_flags/development/project_overview_reorg.yml b/config/feature_flags/development/project_overview_reorg.yml new file mode 100644 index 0000000000000000000000000000000000000000..994c17aada6c124ef2dbdc03404d338ce951ead1 --- /dev/null +++ b/config/feature_flags/development/project_overview_reorg.yml @@ -0,0 +1,8 @@ +--- +name: project_overview_reorg +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137025 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/432034 +milestone: '16.7' +type: development +group: group::ux paper cuts +default_enabled: false diff --git a/ee/spec/features/projects/show_project_spec.rb b/ee/spec/features/projects/show_project_spec.rb index fefb922885b13e9a5509844224544edcf3688247..a6b3ac080477466b32cfe7e7effbee34c4a06fd3 100644 --- a/ee/spec/features/projects/show_project_spec.rb +++ b/ee/spec/features/projects/show_project_spec.rb @@ -36,6 +36,10 @@ create(:project, :repository, mirror: true, mirror_user: user, import_url: 'http://user:pass@test.com') end + before do + stub_feature_flags(project_overview_reorg: false) + end + context 'for maintainer' do before do project.add_maintainer(user) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 453b983fddba2231e627c5faed9548961db30114..3466049ec218d0f5ecccfa78df18c4fe56607377 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -37438,6 +37438,12 @@ msgstr "" msgid "ProjectPage|Project ID: %{project_id}" msgstr "" +msgid "ProjectPage|Project information" +msgstr "" + +msgid "ProjectPage|Project settings" +msgstr "" + msgid "ProjectQualitySummary|An error occurred while trying to fetch project quality statistics" msgstr "" diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb index 3223b2b488ddd311514f1d2c10a35c9b1f3f6a31..93383a8daa0af91d9258b7cfd9ae9af4eddcec40 100644 --- a/qa/qa/page/project/show.rb +++ b/qa/qa/page/project/show.rb @@ -36,6 +36,9 @@ class Show < Page::Base view 'app/views/projects/_home_panel.html.haml' do element 'project-name-content' element 'project-id-content' + end + + view 'app/views/projects/_sidebar.html.haml' do element 'project-badges-content' element 'badge-image-link' end diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb index 5e6857843a672104aaed89c4b0a2f3299d82e971..4933b3f239c6b5a1183645351344ec7b34f8816a 100644 --- a/spec/features/projects/show/user_sees_git_instructions_spec.rb +++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb @@ -10,6 +10,7 @@ # validation failure on NotificationSetting. # See https://gitlab.com/gitlab-org/gitlab/-/issues/299822#note_492817174 user.notification_settings.reset + stub_feature_flags(project_overview_reorg: false) end shared_examples_for 'redirects to the sign in page' do diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb index 41eab966895362022f9833d2c639de500c20493d..674c7db83f1b3f39be8f4621f377b19c4aa147e9 100644 --- a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb +++ b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb @@ -17,8 +17,8 @@ describe 'as a normal user' do before do + stub_feature_flags(project_overview_reorg: false) sign_in(user) - visit project_path(project) end @@ -40,6 +40,7 @@ describe 'as a maintainer' do before do + stub_feature_flags(project_overview_reorg: false) project.add_maintainer(user) sign_in(user) diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index c6966e47f0ac83a701dd4df1799f82abbf98b94e..77ba5c53a35ebf0a3abe6e8d65762bd9111d5817 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -12,6 +12,7 @@ before do sign_in user visit new_project_path + stub_feature_flags(project_overview_reorg: false) end shared_examples 'creates from template' do |template, sub_template_tab = nil| @@ -99,6 +100,7 @@ before do sign_in(project.first_owner) + stub_feature_flags(project_overview_reorg: false) end it 'parses Markdown' do @@ -164,6 +166,7 @@ before do sign_in(project.first_owner) visit path + stub_feature_flags(project_overview_reorg: false) end it 'shows project topics' do @@ -195,6 +198,7 @@ before do sign_in(project.first_owner) visit path + stub_feature_flags(project_overview_reorg: false) end context 'desktop component' do @@ -427,6 +431,10 @@ let(:project) { create(:project, :repository) } let(:user) { create(:user) } + before do + stub_feature_flags(project_overview_reorg: false) + end + it 'does not contain default branch information in its content', :js do default_branch = 'merge-commit-analyze-side-branch' diff --git a/spec/frontend/read_more_spec.js b/spec/frontend/read_more_spec.js index 5f7bd32e23101b47d1fc5b9ddd18f371a4bf8853..9b25c56f19322d8ad768e77c90748027f49acc04 100644 --- a/spec/frontend/read_more_spec.js +++ b/spec/frontend/read_more_spec.js @@ -1,4 +1,3 @@ -import htmlProjectsOverview from 'test_fixtures/projects/overview.html'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; import initReadMore from '~/read_more'; @@ -11,7 +10,12 @@ describe('Read more click-to-expand functionality', () => { describe('expands target element', () => { beforeEach(() => { - setHTMLFixture(htmlProjectsOverview); + setHTMLFixture(` + <p class="read-more-container">Target</p> + <button type="button" class="js-read-more-trigger"> + <span>Button text</span> + </button> + `); }); it('adds "is-expanded" class to target element', () => { diff --git a/spec/helpers/stat_anchors_helper_spec.rb b/spec/helpers/stat_anchors_helper_spec.rb index f3830bf4172120d94bea1947c08ebe40ff963767..41ac7509c39f97030cee7147120c0b5a295b8d69 100644 --- a/spec/helpers/stat_anchors_helper_spec.rb +++ b/spec/helpers/stat_anchors_helper_spec.rb @@ -8,6 +8,10 @@ describe '#stat_anchor_attrs' do subject { helper.stat_anchor_attrs(anchor) } + before do + stub_feature_flags(project_overview_reorg: false) + end + context 'when anchor is a link' do let(:anchor) { anchor_klass.new(true) } diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb index 48db41ea8e34403fa9eecbead572171e10820837..d7738023805b4717acc7e33cca4be6b001a5a793 100644 --- a/spec/presenters/project_presenter_spec.rb +++ b/spec/presenters/project_presenter_spec.rb @@ -428,6 +428,10 @@ end describe '#new_file_anchor_data' do + before do + stub_feature_flags(project_overview_reorg: false) + end + it 'returns new file data if user can push' do project.add_developer(user) @@ -751,6 +755,7 @@ subject(:empty_repo_statistics_buttons) { presenter.empty_repo_statistics_buttons } before do + stub_feature_flags(project_overview_reorg: false) allow(project).to receive(:auto_devops_enabled?).and_return(false) end diff --git a/spec/views/projects/_files.html.haml_spec.rb b/spec/views/projects/_files.html.haml_spec.rb index 96c6c2bdfab8d2f9b2010b483ffd8d051c0e7378..870d436ca888443f512e78f348a364dc5bdd434c 100644 --- a/spec/views/projects/_files.html.haml_spec.rb +++ b/spec/views/projects/_files.html.haml_spec.rb @@ -35,6 +35,8 @@ before do allow(view).to receive(:current_user).and_return(user) allow(user).to receive(:project_shortcut_buttons).and_return(true) + + stub_feature_flags(project_overview_reorg: false) end it 'renders buttons' do @@ -45,6 +47,10 @@ end context 'when rendered in the project overview page and there is no current user' do + before do + stub_feature_flags(project_overview_reorg: false) + end + it 'renders buttons' do render(template, is_project_overview: true) diff --git a/spec/views/projects/_home_panel.html.haml_spec.rb b/spec/views/projects/_home_panel.html.haml_spec.rb index e5081df4c22610a0dc6e66dec3bd2f9ca0576395..a35c87cb4b33c53a81fd57bce0f645f40ea93f46 100644 --- a/spec/views/projects/_home_panel.html.haml_spec.rb +++ b/spec/views/projects/_home_panel.html.haml_spec.rb @@ -100,6 +100,8 @@ allow(view).to receive(:current_user).and_return(user) allow(view).to receive(:can?).with(user, :read_project, project).and_return(false) allow(project).to receive(:license_anchor_data).and_return(false) + + stub_feature_flags(project_overview_reorg: false) end context 'has no badges' do