diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-12 00:08:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-12 00:08:47 +0300 |
commit | 23790cda2b4145294d71591515acfe892d4c99a7 (patch) | |
tree | 0efc35c3f6ae5830953f09929e77569e9082104b /app/assets | |
parent | 413b7041032d2bf34d95cdc1d2375dbf2dbf4163 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets')
8 files changed, 240 insertions, 130 deletions
diff --git a/app/assets/javascripts/organizations/groups_and_projects/graphql/queries/projects.query.graphql b/app/assets/javascripts/organizations/groups_and_projects/graphql/queries/projects.query.graphql index b4cb8c607d4..2a7971e1106 100644 --- a/app/assets/javascripts/organizations/groups_and_projects/graphql/queries/projects.query.graphql +++ b/app/assets/javascripts/organizations/groups_and_projects/graphql/queries/projects.query.graphql @@ -15,6 +15,7 @@ query getOrganizationProjects { descriptionHtml issuesAccessLevel forkingAccessLevel + isForked accessLevel { integerValue } diff --git a/app/assets/javascripts/organizations/groups_and_projects/utils.js b/app/assets/javascripts/organizations/groups_and_projects/utils.js index cbfa120503d..d2a4e05e806 100644 --- a/app/assets/javascripts/organizations/groups_and_projects/utils.js +++ b/app/assets/javascripts/organizations/groups_and_projects/utils.js @@ -1,7 +1,8 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils'; +import { ACTION_EDIT, ACTION_DELETE } from '~/vue_shared/components/projects_list/constants'; export const formatProjects = (projects) => - projects.map(({ id, nameWithNamespace, accessLevel, ...project }) => ({ + projects.map(({ id, nameWithNamespace, accessLevel, webUrl, ...project }) => ({ ...project, id: getIdFromGraphQLId(id), name: nameWithNamespace, @@ -10,6 +11,9 @@ export const formatProjects = (projects) => accessLevel: accessLevel.integerValue, }, }, + webUrl, + editPath: `${webUrl}/edit`, + actions: [ACTION_EDIT, ACTION_DELETE], })); export const formatGroups = (groups) => diff --git a/app/assets/javascripts/projects/components/shared/delete_modal.vue b/app/assets/javascripts/projects/components/shared/delete_modal.vue index aded11ca92c..d15f51692e7 100644 --- a/app/assets/javascripts/projects/components/shared/delete_modal.vue +++ b/app/assets/javascripts/projects/components/shared/delete_modal.vue @@ -37,22 +37,22 @@ export default { required: true, }, issuesCount: { - type: Number, + type: [Number, String], required: false, default: null, }, mergeRequestsCount: { - type: Number, + type: [Number, String], required: false, default: null, }, forksCount: { - type: Number, + type: [Number, String], required: false, default: null, }, starsCount: { - type: Number, + type: [Number, String], required: false, default: null, }, diff --git a/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue index b8e7e9e15db..a02a33992b5 100644 --- a/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue +++ b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue @@ -1,5 +1,5 @@ <script> -import { GlAlert, GlToggle } from '@gitlab/ui'; +import { GlAlert, GlLink, GlToggle, GlSprintf } from '@gitlab/ui'; import axios from '~/lib/utils/axios_utils'; import { __, s__ } from '~/locale'; import { CC_VALIDATION_REQUIRED_ERROR } from '../constants'; @@ -15,7 +15,9 @@ export default { }, components: { GlAlert, + GlLink, GlToggle, + GlSprintf, CcValidationRequiredAlert: () => import('ee_component/billings/components/cc_validation_required_alert.vue'), }, @@ -36,6 +38,16 @@ export default { type: String, required: true, }, + groupName: { + type: String, + required: false, + default: null, + }, + groupSettingsPath: { + type: String, + required: false, + default: null, + }, }, data() { return { @@ -57,6 +69,9 @@ export default { !this.ccAlertDismissed ); }, + isGroupSettingsAvailable() { + return this.groupSettingsPath && this.groupName; + }, }, methods: { creditCardValidated() { @@ -103,16 +118,6 @@ export default { {{ errorMessage }} </gl-alert> - <gl-alert - v-if="isDisabledAndUnoverridable" - data-testid="unoverridable-alert" - variant="warning" - :dismissible="false" - class="gl-mb-5" - > - {{ s__('Runners|Shared runners are disabled in the group settings') }} - </gl-alert> - <gl-toggle ref="sharedRunnersToggle" :disabled="isDisabledAndUnoverridable" @@ -121,7 +126,19 @@ export default { :value="isSharedRunnerEnabled" data-testid="toggle-shared-runners" @change="toggleSharedRunners" - /> + > + <template v-if="isDisabledAndUnoverridable" #help> + {{ s__('Runners|Shared runners are disabled in the group settings.') }} + <gl-sprintf + v-if="isGroupSettingsAvailable" + :message="s__('Runners|Go to %{groupLink} to enable them.')" + > + <template #groupLink> + <gl-link :href="groupSettingsPath">{{ groupName }}</gl-link> + </template> + </gl-sprintf> + </template> + </gl-toggle> </section> </div> </template> diff --git a/app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js b/app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js index 54120b3525d..ace5fd5c6e4 100644 --- a/app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js +++ b/app/assets/javascripts/projects/settings/mount_shared_runners_toggle.js @@ -9,10 +9,15 @@ export default (containerId = 'toggle-shared-runners-form') => { } const { + // required isDisabledAndUnoverridable, isEnabled, updatePath, isCreditCardValidationRequired, + + // optional + groupName, + groupSettingsPath, } = containerEl.dataset; return new Vue({ @@ -24,6 +29,9 @@ export default (containerId = 'toggle-shared-runners-form') => { isEnabled: parseBoolean(isEnabled), isCreditCardValidationRequired: parseBoolean(isCreditCardValidationRequired), updatePath, + + groupName, + groupSettingsPath, }, }); }, diff --git a/app/assets/javascripts/vue_shared/components/projects_list/constants.js b/app/assets/javascripts/vue_shared/components/projects_list/constants.js new file mode 100644 index 00000000000..aa0b1418a06 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/projects_list/constants.js @@ -0,0 +1,2 @@ +export const ACTION_EDIT = 'edit'; +export const ACTION_DELETE = 'delete'; diff --git a/app/assets/javascripts/vue_shared/components/projects_list/projects_list.vue b/app/assets/javascripts/vue_shared/components/projects_list/projects_list.vue index cb8220a0407..3a4da54c84c 100644 --- a/app/assets/javascripts/vue_shared/components/projects_list/projects_list.vue +++ b/app/assets/javascripts/vue_shared/components/projects_list/projects_list.vue @@ -46,6 +46,7 @@ export default { :key="project.id" :project="project" :show-project-icon="showProjectIcon" + @delete="$emit('delete', $event)" /> </ul> </template> diff --git a/app/assets/javascripts/vue_shared/components/projects_list/projects_list_item.vue b/app/assets/javascripts/vue_shared/components/projects_list/projects_list_item.vue index d919f76e684..9fc4571b0dc 100644 --- a/app/assets/javascripts/vue_shared/components/projects_list/projects_list_item.vue +++ b/app/assets/javascripts/vue_shared/components/projects_list/projects_list_item.vue @@ -7,6 +7,7 @@ import { GlTooltipDirective, GlPopover, GlSprintf, + GlDisclosureDropdown, } from '@gitlab/ui'; import uniqueId from 'lodash/uniqueId'; @@ -19,6 +20,8 @@ import { numberToMetricPrefix } from '~/lib/utils/number_utils'; import { truncate } from '~/lib/utils/text_utility'; import SafeHtml from '~/vue_shared/directives/safe_html'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import DeleteModal from '~/projects/components/shared/delete_modal.vue'; +import { ACTION_EDIT, ACTION_DELETE } from './constants'; const MAX_TOPICS_TO_SHOW = 3; const MAX_TOPIC_TITLE_LENGTH = 15; @@ -33,6 +36,7 @@ export default { topicsPopoverTargetText: __('+ %{count} more'), moreTopics: __('More topics'), updated: __('Updated'), + actions: __('Actions'), }, avatarSize: { default: 32, md: 48 }, safeHtmlConfig: { @@ -47,6 +51,8 @@ export default { GlPopover, GlSprintf, TimeAgoTooltip, + GlDisclosureDropdown, + DeleteModal, }, directives: { GlTooltip: GlTooltipDirective, @@ -73,6 +79,9 @@ export default { * }; * descriptionHtml: string; * updatedAt: string; + * isForked: boolean; + * actions?: ('edit' | 'delete')[]; + * editPath?: string; * } */ project: { @@ -88,6 +97,7 @@ export default { data() { return { topicsPopoverTarget: uniqueId('project-topics-popover-'), + isDeleteModalVisible: false, }; }, computed: { @@ -136,9 +146,50 @@ export default { popoverTopics() { return this.project.topics.slice(MAX_TOPICS_TO_SHOW); }, + starCount() { + return numberToMetricPrefix(this.project.starCount); + }, + forksCount() { + if (!this.isForkingEnabled) { + return null; + } + + return numberToMetricPrefix(this.project.forksCount); + }, + openIssuesCount() { + if (!this.isIssuesEnabled) { + return null; + } + + return numberToMetricPrefix(this.project.openIssuesCount); + }, + actionsDropdownItems() { + return [ + { + id: ACTION_EDIT, + text: __('Edit'), + href: this.project.editPath, + }, + { + id: ACTION_DELETE, + text: __('Delete'), + extraAttrs: { + class: 'gl-text-red-500!', + }, + action: () => { + this.isDeleteModalVisible = true; + }, + }, + ].filter(({ id }) => this.project.actions?.includes(id)); + }, + hasActions() { + return this.actionsDropdownItems.length; + }, + hasDeleteAction() { + return this.actionsDropdownItems.find((action) => action.id === ACTION_DELETE); + }, }, methods: { - numberToMetricPrefix, topicPath(topic) { return `/explore/projects/topics/${encodeURIComponent(topic)}`; }, @@ -158,128 +209,154 @@ export default { </script> <template> - <li class="projects-list-item gl-py-5 gl-md-display-flex gl-align-items-center gl-border-b"> - <div class="gl-display-flex gl-flex-grow-1"> - <gl-icon - v-if="showProjectIcon" - class="gl-mr-3 gl-mt-3 gl-md-mt-5 gl-flex-shrink-0 gl-text-secondary" - name="project" - /> - <gl-avatar-labeled - :entity-id="project.id" - :entity-name="project.name" - :label="project.name" - :label-link="project.webUrl" - shape="rect" - :size="$options.avatarSize" - > - <template #meta> - <div class="gl-px-2"> - <div class="gl-mx-n2 gl-display-flex gl-align-items-center gl-flex-wrap"> - <div class="gl-px-2"> - <gl-icon - v-if="visibility" - v-gl-tooltip="visibilityTooltip" - :name="visibilityIcon" - class="gl-text-secondary" - /> - </div> - <div class="gl-px-2"> - <user-access-role-badge v-if="shouldShowAccessLevel">{{ - accessLevelLabel - }}</user-access-role-badge> + <li class="projects-list-item gl-py-5 gl-border-b gl-display-flex gl-align-items-flex-start"> + <div class="gl-md-display-flex gl-align-items-center gl-flex-grow-1"> + <div class="gl-display-flex gl-flex-grow-1"> + <gl-icon + v-if="showProjectIcon" + class="gl-mr-3 gl-mt-3 gl-md-mt-5 gl-flex-shrink-0 gl-text-secondary" + name="project" + /> + <gl-avatar-labeled + :entity-id="project.id" + :entity-name="project.name" + :label="project.name" + :label-link="project.webUrl" + shape="rect" + :size="$options.avatarSize" + > + <template #meta> + <div class="gl-px-2"> + <div class="gl-mx-n2 gl-display-flex gl-align-items-center gl-flex-wrap"> + <div class="gl-px-2"> + <gl-icon + v-if="visibility" + v-gl-tooltip="visibilityTooltip" + :name="visibilityIcon" + class="gl-text-secondary" + /> + </div> + <div class="gl-px-2"> + <user-access-role-badge v-if="shouldShowAccessLevel">{{ + accessLevelLabel + }}</user-access-role-badge> + </div> </div> </div> - </div> - </template> - <div - v-if="project.descriptionHtml" - v-safe-html:[$options.safeHtmlConfig]="project.descriptionHtml" - class="gl-font-sm gl-overflow-hidden gl-line-height-20 description md" - data-testid="project-description" - ></div> - <div v-if="hasTopics" class="gl-mt-3" data-testid="project-topics"> + </template> <div - class="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" - > - <span class="gl-p-2 gl-text-secondary">{{ $options.i18n.topics }}:</span> - <div v-for="topic in visibleTopics" :key="topic" class="gl-p-2"> - <gl-badge v-gl-tooltip="topicTooltipTitle(topic)" :href="topicPath(topic)"> - {{ topicTitle(topic) }} - </gl-badge> - </div> - <template v-if="popoverTopics.length"> - <div - :id="topicsPopoverTarget" - class="gl-p-2 gl-text-secondary" - role="button" - tabindex="0" - > - <gl-sprintf :message="$options.i18n.topicsPopoverTargetText"> - <template #count>{{ popoverTopics.length }}</template> - </gl-sprintf> + v-if="project.descriptionHtml" + v-safe-html:[$options.safeHtmlConfig]="project.descriptionHtml" + class="gl-font-sm gl-overflow-hidden gl-line-height-20 description md" + data-testid="project-description" + ></div> + <div v-if="hasTopics" class="gl-mt-3" data-testid="project-topics"> + <div + class="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" + > + <span class="gl-p-2 gl-text-secondary">{{ $options.i18n.topics }}:</span> + <div v-for="topic in visibleTopics" :key="topic" class="gl-p-2"> + <gl-badge v-gl-tooltip="topicTooltipTitle(topic)" :href="topicPath(topic)"> + {{ topicTitle(topic) }} + </gl-badge> </div> - <gl-popover :target="topicsPopoverTarget" :title="$options.i18n.moreTopics"> - <div class="gl-font-base gl-font-weight-normal gl-mx-n2 gl-my-n2"> - <div - v-for="topic in popoverTopics" - :key="topic" - class="gl-p-2 gl-display-inline-block" - > - <gl-badge v-gl-tooltip="topicTooltipTitle(topic)" :href="topicPath(topic)"> - {{ topicTitle(topic) }} - </gl-badge> - </div> + <template v-if="popoverTopics.length"> + <div + :id="topicsPopoverTarget" + class="gl-p-2 gl-text-secondary" + role="button" + tabindex="0" + > + <gl-sprintf :message="$options.i18n.topicsPopoverTargetText"> + <template #count>{{ popoverTopics.length }}</template> + </gl-sprintf> </div> - </gl-popover> - </template> + <gl-popover :target="topicsPopoverTarget" :title="$options.i18n.moreTopics"> + <div class="gl-font-base gl-font-weight-normal gl-mx-n2 gl-my-n2"> + <div + v-for="topic in popoverTopics" + :key="topic" + class="gl-p-2 gl-display-inline-block" + > + <gl-badge v-gl-tooltip="topicTooltipTitle(topic)" :href="topicPath(topic)"> + {{ topicTitle(topic) }} + </gl-badge> + </div> + </div> + </gl-popover> + </template> + </div> </div> - </div> - </gl-avatar-labeled> - </div> - <div - class="gl-md-display-flex gl-flex-direction-column gl-align-items-flex-end gl-flex-shrink-0 gl-mt-3 gl-md-pl-0 gl-md-mt-0" - :class="showProjectIcon ? 'gl-pl-11' : 'gl-pl-8'" - > - <div class="gl-display-flex gl-align-items-center gl-gap-x-3"> - <gl-badge v-if="project.archived" variant="warning">{{ $options.i18n.archived }}</gl-badge> - <gl-link - v-gl-tooltip="$options.i18n.stars" - :href="starsHref" - :aria-label="$options.i18n.stars" - class="gl-text-secondary" - > - <gl-icon name="star-o" /> - <span>{{ numberToMetricPrefix(project.starCount) }}</span> - </gl-link> - <gl-link - v-if="isForkingEnabled" - v-gl-tooltip="$options.i18n.forks" - :href="forksHref" - :aria-label="$options.i18n.forks" - class="gl-text-secondary" - > - <gl-icon name="fork" /> - <span>{{ numberToMetricPrefix(project.forksCount) }}</span> - </gl-link> - <gl-link - v-if="isIssuesEnabled" - v-gl-tooltip="$options.i18n.issues" - :href="issuesHref" - :aria-label="$options.i18n.issues" - class="gl-text-secondary" - > - <gl-icon name="issues" /> - <span>{{ numberToMetricPrefix(project.openIssuesCount) }}</span> - </gl-link> + </gl-avatar-labeled> </div> <div - v-if="project.updatedAt" - class="gl-font-sm gl-white-space-nowrap gl-text-secondary gl-mt-3" + class="gl-md-display-flex gl-flex-direction-column gl-align-items-flex-end gl-flex-shrink-0 gl-mt-3 gl-md-pl-0 gl-md-mt-0" + :class="showProjectIcon ? 'gl-pl-11' : 'gl-pl-8'" > - <span>{{ $options.i18n.updated }}</span> - <time-ago-tooltip :time="project.updatedAt" /> + <div class="gl-display-flex gl-align-items-center gl-gap-x-3"> + <gl-badge v-if="project.archived" variant="warning">{{ + $options.i18n.archived + }}</gl-badge> + <gl-link + v-gl-tooltip="$options.i18n.stars" + :href="starsHref" + :aria-label="$options.i18n.stars" + class="gl-text-secondary" + > + <gl-icon name="star-o" /> + <span>{{ starCount }}</span> + </gl-link> + <gl-link + v-if="isForkingEnabled" + v-gl-tooltip="$options.i18n.forks" + :href="forksHref" + :aria-label="$options.i18n.forks" + class="gl-text-secondary" + > + <gl-icon name="fork" /> + <span>{{ forksCount }}</span> + </gl-link> + <gl-link + v-if="isIssuesEnabled" + v-gl-tooltip="$options.i18n.issues" + :href="issuesHref" + :aria-label="$options.i18n.issues" + class="gl-text-secondary" + > + <gl-icon name="issues" /> + <span>{{ openIssuesCount }}</span> + </gl-link> + </div> + <div + v-if="project.updatedAt" + class="gl-font-sm gl-white-space-nowrap gl-text-secondary gl-mt-3" + > + <span>{{ $options.i18n.updated }}</span> + <time-ago-tooltip :time="project.updatedAt" /> + </div> </div> </div> + <gl-disclosure-dropdown + v-if="hasActions" + class="gl-ml-3 gl-md-align-self-center" + :items="actionsDropdownItems" + icon="ellipsis_v" + no-caret + :toggle-text="$options.i18n.actions" + text-sr-only + placement="right" + category="tertiary" + /> + + <delete-modal + v-if="hasDeleteAction" + v-model="isDeleteModalVisible" + :confirm-phrase="project.name" + :is-fork="project.isForked" + :issues-count="openIssuesCount" + :forks-count="forksCount" + :stars-count="starCount" + @primary="$emit('delete', project)" + /> </li> </template> |