diff --git a/frontend/src/modules/contributor/components/details/contributor-details-work-history.vue b/frontend/src/modules/contributor/components/details/contributor-details-work-history.vue
index e6d5779321..b19d8b10fa 100644
--- a/frontend/src/modules/contributor/components/details/contributor-details-work-history.vue
+++ b/frontend/src/modules/contributor/components/details/contributor-details-work-history.vue
@@ -31,14 +31,17 @@
-
-
+
+
+
+
+
+
No work experiences
@@ -55,7 +58,7 @@
(false);
const isEditModalOpen = ref(false);
const editOrganization = ref(null);
+const hoveredGroup = ref(null);
-const orgs = computed(() => props.contributor.organizations);
+const orgGrouped = computed(() => {
+ const grouped = groupBy(props.contributor.organizations, 'id');
+ return Object.keys(grouped).map((id, index): TimelineGroup => ({
+ id: index,
+ label: grouped[id][0].displayName,
+ labelLink: {
+ name: 'organizationView',
+ params: {
+ id,
+ },
+ query: {
+ projectGroup: selectedProjectGroup.value?.id,
+ },
+ },
+ icon: grouped[id][0].logo,
+ items: grouped[id],
+ }));
+});
+const minimumShownGroups = 3;
+const shownGroups = computed(() => orgGrouped.value.slice(0, showMore.value ? orgGrouped.value.length : minimumShownGroups));
const masked = computed(() => isMasked(props.contributor));
+
+const onGroupHover = (index: TimelineGroup | null) => {
+ hoveredGroup.value = index;
+};
+
+
+
+
diff --git a/frontend/src/ui-kit/timeline/TimelineItem.vue b/frontend/src/ui-kit/timeline/TimelineItem.vue
new file mode 100644
index 0000000000..f0fa40641c
--- /dev/null
+++ b/frontend/src/ui-kit/timeline/TimelineItem.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/frontend/src/ui-kit/timeline/timeline.scss b/frontend/src/ui-kit/timeline/timeline.scss
new file mode 100644
index 0000000000..747aff3e08
--- /dev/null
+++ b/frontend/src/ui-kit/timeline/timeline.scss
@@ -0,0 +1,40 @@
+.c-timeline {
+ @apply flex items-start gap-3;
+ &__group-label {
+ @apply flex items-center font-semibold text-medium leading-6 mb-1 truncate text-black hover:text-primary-500 transition block w-full overflow-hidden;
+ }
+ // &__border {
+ // @apply w-px h-full bg-gray-200;
+ // }
+
+ ::v-deep .c-timeline__item {
+ @apply relative mb-4;
+ .c-timeline__item-dot {
+ @apply w-3 h-3 z-[2] bg-gray-200 rounded-full absolute top-[2px] -left-[30px] border-3 border-white;
+ }
+ .c-timeline__item-content {
+ @apply relative;
+ &::before {
+ content: '';
+ @apply w-px bg-gray-200 absolute top-[2px] left-[-25px];
+ height: calc(100% + 25px);
+ }
+ }
+
+ &:first-child {
+ .c-timeline__item-dot {
+ @apply invisible;
+ }
+ }
+
+ &:last-child {
+ .c-timeline__item-content {
+ &::before {
+ @apply h-[5px];
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/frontend/src/ui-kit/timeline/types/TimelineTypes.ts b/frontend/src/ui-kit/timeline/types/TimelineTypes.ts
new file mode 100644
index 0000000000..9db85110c1
--- /dev/null
+++ b/frontend/src/ui-kit/timeline/types/TimelineTypes.ts
@@ -0,0 +1,9 @@
+import type { RouteLocationRaw } from 'vue-router';
+
+export interface TimelineGroup {
+ id: number;
+ label: string;
+ labelLink?: RouteLocationRaw;
+ icon?: string;
+ items: any[];
+}
diff --git a/services/apps/merge_suggestions_worker/src/activities/common.ts b/services/apps/merge_suggestions_worker/src/activities/common.ts
index c55cfdb9e4..6f10a56e24 100644
--- a/services/apps/merge_suggestions_worker/src/activities/common.ts
+++ b/services/apps/merge_suggestions_worker/src/activities/common.ts
@@ -131,7 +131,7 @@ export async function mergeMembers(
try {
await axios(url, requestOptions)
} catch (error) {
- console.log(`Failed merging member wit status [${error.response.status}]. Skipping!`)
+ console.log(`Failed merging member wit status [${error.response?.status}]. Skipping!`)
}
}
@@ -156,6 +156,6 @@ export async function mergeOrganizations(
try {
await axios(url, requestOptions)
} catch (error) {
- console.log(`Failed merging organization with status [${error.response.status}]. Skipping!`)
+ console.log(`Failed merging organization with status [${error.response?.status}]. Skipping!`)
}
}