Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/work_items_hierarchy/components')
-rw-r--r--app/assets/javascripts/work_items_hierarchy/components/app.vue101
-rw-r--r--app/assets/javascripts/work_items_hierarchy/components/hierarchy.vue119
2 files changed, 220 insertions, 0 deletions
diff --git a/app/assets/javascripts/work_items_hierarchy/components/app.vue b/app/assets/javascripts/work_items_hierarchy/components/app.vue
new file mode 100644
index 00000000000..621cfe5bace
--- /dev/null
+++ b/app/assets/javascripts/work_items_hierarchy/components/app.vue
@@ -0,0 +1,101 @@
+<script>
+import { GlBanner } from '@gitlab/ui';
+import Cookies from 'js-cookie';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import RESPONSE from '../static_response';
+import { WORK_ITEMS_SURVEY_COOKIE_NAME, workItemTypes } from '../constants';
+import Hierarchy from './hierarchy.vue';
+
+export default {
+ components: {
+ GlBanner,
+ Hierarchy,
+ },
+ inject: ['illustrationPath', 'licensePlan'],
+ data() {
+ return {
+ bannerVisible: !parseBoolean(Cookies.get(WORK_ITEMS_SURVEY_COOKIE_NAME)),
+ workItemHierarchy: RESPONSE[this.licensePlan],
+ };
+ },
+ computed: {
+ hasUnavailableStructure() {
+ return this.workItemTypes.unavailable.length > 0;
+ },
+ workItemTypes() {
+ return this.workItemHierarchy.reduce(
+ (itemTypes, item) => {
+ const skipItem = workItemTypes[item.type].isWorkItem && !window.gon?.features?.workItems;
+
+ if (skipItem) {
+ return itemTypes;
+ }
+ const key = item.available ? 'available' : 'unavailable';
+ const nestedTypes = item.nestedTypes?.map((type) => workItemTypes[type]);
+
+ itemTypes[key].push({
+ ...item,
+ ...workItemTypes[item.type],
+ nestedTypes,
+ });
+
+ return itemTypes;
+ },
+ { available: [], unavailable: [] },
+ );
+ },
+ },
+ methods: {
+ handleClose() {
+ Cookies.set(WORK_ITEMS_SURVEY_COOKIE_NAME, 'true', { expires: 365 * 10 });
+ this.bannerVisible = false;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-banner
+ v-if="bannerVisible"
+ class="gl-mt-4 gl-px-5!"
+ :title="s__('Hierarchy|Help us improve work items in GitLab!')"
+ :button-text="s__('Hierarchy|Take the work items survey')"
+ button-link="https://forms.gle/u1BmRp8rTbwj52iq5"
+ :svg-path="illustrationPath"
+ @close="handleClose"
+ >
+ <p>
+ {{
+ s__(
+ 'Hierarchy|Is there a framework or type of work item you wish you had access to in GitLab? Give us your feedback and help us build the experiences valuable to you.',
+ )
+ }}
+ </p>
+ </gl-banner>
+ <h3 class="gl-mt-5!">{{ s__('Hierarchy|Planning hierarchy') }}</h3>
+ <p>
+ {{
+ s__(
+ 'Hierarchy|Deliver value more efficiently by breaking down necessary work into a hierarchical structure. This structure helps teams understand scope, priorities, and how work cascades up toward larger goals.',
+ )
+ }}
+ </p>
+
+ <div class="gl-font-weight-bold gl-mb-2">{{ s__('Hierarchy|Current structure') }}</div>
+ <p class="gl-mb-3!">{{ s__('Hierarchy|You can start using these items now.') }}</p>
+ <hierarchy :work-item-types="workItemTypes.available" />
+
+ <div
+ v-if="hasUnavailableStructure"
+ data-testid="unavailable-structure"
+ class="gl-font-weight-bold gl-mt-5 gl-mb-2"
+ >
+ {{ s__('Hierarchy|Unavailable structure') }}
+ </div>
+ <p v-if="hasUnavailableStructure" class="gl-mb-3!">
+ {{ s__('Hierarchy|These items are unavailable in the current structure.') }}
+ </p>
+ <hierarchy :work-item-types="workItemTypes.unavailable" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/work_items_hierarchy/components/hierarchy.vue b/app/assets/javascripts/work_items_hierarchy/components/hierarchy.vue
new file mode 100644
index 00000000000..9b81218b6e4
--- /dev/null
+++ b/app/assets/javascripts/work_items_hierarchy/components/hierarchy.vue
@@ -0,0 +1,119 @@
+<script>
+import { GlIcon, GlBadge } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlIcon,
+ GlBadge,
+ },
+ props: {
+ workItemTypes: {
+ type: Array,
+ required: true,
+ },
+ },
+ methods: {
+ isLastItem(index, workItem) {
+ const hasMoreThanOneItem = workItem.nestedTypes.length > 1;
+ const isLastItemInArray = index === workItem.nestedTypes.length - 1;
+
+ return isLastItemInArray && hasMoreThanOneItem;
+ },
+ nestedWorkItemTypeMargin(index, workItem) {
+ const isLastItemInArray = index === workItem.nestedTypes.length - 1;
+ const hasMoreThanOneItem = workItem.nestedTypes.length > 1;
+
+ if (isLastItemInArray && hasMoreThanOneItem) {
+ return 'gl-ml-0';
+ }
+
+ return 'gl-ml-6';
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <div
+ v-for="workItem in workItemTypes"
+ :key="workItem.id"
+ class="gl-mb-3"
+ :class="{ flex: !workItem.available }"
+ >
+ <span
+ class="gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-base gl-pl-2 gl-pt-2 gl-pb-2 gl-pr-3 gl-display-inline-flex gl-align-items-center gl-justify-content-center gl-line-height-normal"
+ data-testid="work-item-wrapper"
+ >
+ <span
+ :style="{
+ backgroundColor: workItem.backgroundColor,
+ color: workItem.color,
+ }"
+ class="gl-rounded-base gl-mr-2 gl-display-inline-flex justify-content-center align-items-center hierarchy-icon-wrapper"
+ >
+ <gl-icon :size="workItem.iconSize || 12" :name="workItem.icon" />
+ </span>
+
+ {{ workItem.title }}
+ </span>
+
+ <gl-badge
+ v-if="!workItem.available"
+ variant="info"
+ icon="license"
+ size="sm"
+ class="gl-ml-3 gl-align-self-center"
+ >{{ workItem.license }}</gl-badge
+ >
+
+ <div v-if="workItem.nestedTypes" :class="{ 'gl-relative': workItem.nestedTypes.length > 1 }">
+ <svg
+ v-if="workItem.nestedTypes.length > 1"
+ class="hierarchy-rounded-arrow-tail gl-text-gray-400"
+ data-testid="hierarchy-rounded-arrow-tail"
+ width="2"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <line
+ x1="0.75"
+ y1="1"
+ x2="0.75"
+ y2="100%"
+ stroke="currentColor"
+ stroke-width="1.5"
+ stroke-linecap="round"
+ />
+ </svg>
+ <template v-for="(nestedWorkItem, index) in workItem.nestedTypes">
+ <div :key="nestedWorkItem.id" class="gl-display-block gl-mt-2 gl-ml-6">
+ <gl-icon name="arrow-down" class="gl-text-gray-400" />
+ </div>
+ <gl-icon
+ v-if="isLastItem(index, workItem)"
+ :key="nestedWorkItem.id"
+ name="level-up"
+ class="gl-text-gray-400 gl-ml-2 hierarchy-rounded-arrow"
+ />
+ <span
+ :key="nestedWorkItem.id"
+ class="gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-base gl-pl-2 gl-pt-2 gl-pb-2 gl-pr-3 gl-display-inline-flex gl-align-items-center gl-justify-content-center gl-mt-2 gl-line-height-normal"
+ :class="nestedWorkItemTypeMargin(index, workItem)"
+ >
+ <span
+ :style="{
+ backgroundColor: nestedWorkItem.backgroundColor,
+ color: nestedWorkItem.color,
+ }"
+ class="gl-rounded-base gl-mr-2 gl-display-inline-flex justify-content-center align-items-center hierarchy-icon-wrapper"
+ >
+ <gl-icon :size="nestedWorkItem.iconSize || 12" :name="nestedWorkItem.icon" />
+ </span>
+
+ {{ nestedWorkItem.title }}
+ </span>
+ </template>
+ </div>
+ </div>
+ </div>
+</template>