Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@
</div>

<div v-if="!masked" class="flex flex-col gap-4">
<lf-contributor-details-work-history-item
v-for="org of (orgs || []).slice(0, showMore ? (orgs || []).length : 3)"
:key="org.id"
:contributor="props.contributor"
:organization="org"
@edit="isEditModalOpen = true; editOrganization = org"
/>
<div v-if="orgs.length === 0" class="pt-2 flex flex-col items-center">
<lf-timeline v-slot="{ group }" :groups="shownGroups">
<lf-timeline-item v-for="item in group.items" :key="item.id" data="Item 1">
<lf-contributor-details-work-history-item
:contributor="props.contributor"
:organization="item"
@edit="isEditModalOpen = true; editOrganization = item"
/>
</lf-timeline-item>
</lf-timeline>
<div v-if="orgGrouped.length === 0" class="pt-2 flex flex-col items-center">
<lf-icon-old name="survey-line" :size="40" class="text-gray-300" />
<p class="text-center pt-3 text-medium text-gray-400">
No work experiences
Expand All @@ -55,7 +57,7 @@
</div>

<lf-button
v-if="!masked && orgs.length > 3"
v-if="!masked && orgGrouped.length > minimumShownGroups"
type="primary-link"
size="medium"
class="mt-6"
Expand Down Expand Up @@ -88,19 +90,52 @@ import LfContributorDetailsWorkHistoryItem
from '@/modules/contributor/components/details/work-history/contributor-details-work-history-item.vue';
import useContributorHelpers from '@/modules/contributor/helpers/contributor.helpers';
import LfIcon from '@/ui-kit/icon/Icon.vue';
import { TimelineGroup } from '@/ui-kit/timeline/types/TimelineTypes';
import { groupBy } from 'lodash';
import { storeToRefs } from 'pinia';
import { useLfSegmentsStore } from '@/modules/lf/segments/store';
import LfTimeline from '@/ui-kit/timeline/Timeline.vue';
import LfTimelineItem from '@/ui-kit/timeline/TimelineItem.vue';

const props = defineProps<{
contributor: Contributor,
}>();

const { hasPermission } = usePermissions();
const { isMasked } = useContributorHelpers();
const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore());

const showMore = ref<boolean>(false);
const isEditModalOpen = ref<boolean>(false);
const editOrganization = ref<Organization | null>(null);

const orgs = computed(() => props.contributor.organizations);
// const orgs = computed(() => props.contributor.organizations);
const orgGrouped = computed(() => {
const grouped = groupBy(props.contributor.organizations, 'id');
return Object.keys(grouped).map((id): TimelineGroup => ({
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 = computed(() => {
const MIN_ITEMS = 3;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you confirm that this logic is working? If you check the video I have 3 organizations but only 2 are showing.
By default we should always show 3 organizations regardless of how many titles we have for each one of them

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I sent a message through slack regarding this. I reverted this anyway to the old fixed 3 organization limit regardless of how many roles the contributor had in that org.

const groupMinIdx = props.contributor.organizations.length >= MIN_ITEMS ? MIN_ITEMS - 1 : 0;

return orgGrouped.value.length > 0
? orgGrouped.value.findIndex((group) => props.contributor.organizations[groupMinIdx].id === group.items[0].id) + 1
: MIN_ITEMS;
});
const shownGroups = computed(() => orgGrouped.value.slice(0, showMore.value ? orgGrouped.value.length : minimumShownGroups.value));

const masked = computed(() => isMasked(props.contributor));
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,7 @@
@mouseleave="hovered = false"
>
<div class="flex">
<lf-avatar
:name="props.organization.displayName"
:src="props.organization.logo"
:size="24"
class="!rounded-md border border-gray-200 min-w-6"
img-class="!object-contain"
>
<template #placeholder>
<div class="w-full h-full bg-gray-50 flex items-center justify-center">
<lf-icon-old name="community-line" :size="16" class="text-gray-400" />
</div>
</template>
</lf-avatar>

<div class="pl-3 flex flex-auto flex-col overflow-hidden">
<router-link
:to="{
name: 'organizationView',
params: {
id: props.organization.id,
},
query: {
projectGroup: selectedProjectGroup?.id,
},
}"
class="font-semibold text-medium leading-6 mb-1 truncate text-black hover:text-primary-500 transition block w-full overflow-hidden"
>
{{ props.organization.displayName }}
</router-link>

<div class="flex flex-auto flex-col overflow-hidden">
<div v-if="props.organization?.memberOrganizations?.title" class="text-small text-gray-500 mb-1.5 flex items-center gap-1.5">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This color should be updated to text-gray-900

<lf-svg name="id-card" class="h-4 w-4 text-gray-400" />
<p class="truncate" style="max-width: 30ch">
Expand Down Expand Up @@ -80,11 +51,8 @@
import LfIconOld from '@/ui-kit/icon/IconOld.vue';
import { Contributor } from '@/modules/contributor/types/Contributor';
import LfSvg from '@/shared/svg/svg.vue';
import LfAvatar from '@/ui-kit/avatar/Avatar.vue';
import { Organization } from '@/modules/organization/types/Organization';
import moment from 'moment';
import { storeToRefs } from 'pinia';
import { useLfSegmentsStore } from '@/modules/lf/segments/store';
import LfButton from '@/ui-kit/button/Button.vue';
import LfDropdown from '@/ui-kit/dropdown/Dropdown.vue';
import LfDropdownItem from '@/ui-kit/dropdown/DropdownItem.vue';
Expand All @@ -108,7 +76,6 @@ const props = defineProps<{

const emit = defineEmits<{(e:'edit'): void}>();

const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore());
const { deleteContributorOrganization } = useContributorStore();
const { trackEvent } = useProductTracking();

Expand Down
51 changes: 51 additions & 0 deletions frontend/src/ui-kit/timeline/Timeline.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import LfSvg from '@/shared/svg/svg.vue';
import LfIconOld from '@/ui-kit/icon/IconOld.vue';
import LfTimeline from './Timeline.vue';
import LfTimelineItem from './TimelineItem.vue';

export default {
title: 'LinuxFoundation/Timeline',
component: LfTimeline,
tags: ['autodocs'],
argTypes: {
groups: {
description: 'Timeline groups',
control: 'object',
},
},
};

export const Regular = {
args: {
groups: [
{
label: 'Group 1', labelLink: { name: 'organizationView', params: { id: '1' } }, icon: 'https://avatars.githubusercontent.com/u/38015056?v=4', items: [{ id: 1, label: 'Item 1', date: 'Aug 2024 - Dec 2024' }, { id: 2, label: 'Item 2', date: 'Aug 2024' }, { id: 3, label: 'Item 3', date: 'Aug 2024' }, { id: 4, label: 'Item 4', date: 'Aug 2024' }],
},
{
label: 'Group 2', labelLink: { name: 'organizationView', params: { id: '2' } }, icon: 'https://avatars.githubusercontent.com/u/38015056?v=4', items: [{ id: 3, label: 'Item 3', date: 'Aug 2024' }, { id: 4, label: 'Item 4', date: 'Aug 2024' }],
},
],
},
render: (args: any) => ({
components: {
LfTimeline, LfTimelineItem, LfSvg, LfIconOld,
},
setup() {
return { args };
},
template: `<lf-timeline :groups="args.groups" v-slot="{ group }">
<lf-timeline-item data="Item 1" v-for="item in group.items" :key="item.id">
<div class="text-small text-gray-900 mb-1.5 flex items-center gap-1.5">
<lf-svg name="id-card" class="h-4 w-4 text-gray-400" />
<p class="truncate" style="max-width: 30ch">
{{ item.label }}
</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have by default a text-gray-900 color, and only on hover a blue one (link behaviour)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a sample content inside the timeline component. You can theoretically use anything inside the content of the lf-timeline-item. I've added the text-gray-900 anyway.

</div>
<p class="text-small text-gray-500 mb-1.5 flex items-center">
<lf-icon-old name="calendar-line" :size="16" class="mr-1.5 text-gray-400" />
{{ item.date }}
</p>
</lf-timeline-item>
</lf-timeline>`,
}),
};
49 changes: 49 additions & 0 deletions frontend/src/ui-kit/timeline/Timeline.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<template>
<div v-for="group in props.groups" :key="group.label" class="c-timeline">
<div class="c-timeline__group-icon">
<lf-avatar
:name="group.label"
:src="group.icon"
:size="24"
class="!rounded-md border border-gray-200 min-w-6"
img-class="!object-contain"
/>
</div>
<div>
<div class="c-timeline__group-label">
<router-link
v-if="group.labelLink"
:to="group.labelLink"
class="cursor-pointer"
>
{{ group.label }}
</router-link>
<span v-else>
{{ group.label }}
</span>
</div>

<div class="c-timeline__items">
<slot name="default" :group="group" />
</div>
</div>
</div>
</template>

<script lang="ts" setup>
import LfAvatar from '@/ui-kit/avatar/Avatar.vue';
import { TimelineGroup } from './types/TimelineTypes';

const props = defineProps<{
groups: TimelineGroup[];
}>();

</script>

<script lang="ts">
export default {
name: 'LfTimeline',
};
</script>

<style scoped lang="scss" src="./timeline.scss" />
19 changes: 19 additions & 0 deletions frontend/src/ui-kit/timeline/TimelineItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div class="c-timeline__item">
<div class="c-timeline__item-dot" />
<div class="c-timeline__item-content">
<slot :data="props.data" />
</div>
</div>
</template>

<script lang="ts" setup>
const props = defineProps<{
data: any;
}>();
</script>
<script lang="ts">
export default {
name: 'LfTimelineItem',
};
</script>
40 changes: 40 additions & 0 deletions frontend/src/ui-kit/timeline/timeline.scss
Original file line number Diff line number Diff line change
@@ -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];
}
}
}
}
}


8 changes: 8 additions & 0 deletions frontend/src/ui-kit/timeline/types/TimelineTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { RouteLocationRaw } from 'vue-router';

export interface TimelineGroup {
label: string;
labelLink?: RouteLocationRaw;
icon?: string;
items: any[];
}
Loading