Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
<p class="text-small text-gray-500 break-normal text-left">
Organization(s) that an individual represents while contributing to a project.
This association indicates that the person's activities were made in the context
of their role within the organization, rather than as an independent contributor.
of their role within the organization, rather than as an independent
contributor.
</p>
</div>
</el-popover>
Expand All @@ -40,14 +41,15 @@
</p>
<p class="text-small text-gray-500 break-normal mb-5 text-left">
Individual responsible for overseeing and managing code repositories by
reviewing and merging pull requests, addressing issues, ensuring code quality, and guiding contributors.
reviewing and merging pull requests, addressing issues, ensuring code quality,
and guiding contributors.
</p>
<p class="text-small font-semibold mb-2 text-black">
Contributor
</p>
<p class="text-small text-gray-500 break-normal text-left">
Someone who has contributed to a project by making changes or additions to its code.
Contributions require that code was successfully merged into a repository.
Someone who has contributed to a project by making changes or additions to its
code. Contributions require that code was successfully merged into a repository.
</p>
</div>
</el-popover>
Expand All @@ -57,21 +59,26 @@
</thead>

<tbody>
<tr
v-for="project in (projects || []).slice(0, showMore ? (projects || []).length : 3)"
:key="project.id"
>
<tr v-for="project in (projects || []).slice(0, showMore ? (projects || []).length : 3)" :key="project.id">
<lf-table-cell class="pl-5">
<p class="text-medium font-semibold py-1.5">
{{ project.name }}
</p>
<p class="text-small text-gray-500 whitespace-nowrap flex items-center gap-1">
<lf-icon name="monitor-waveform" />{{ project.activityCount }} {{ parseInt(project.activityCount) > 1 ? 'activities' : 'activity' }}
</p>
</lf-table-cell>
<lf-table-cell>
<div v-if="Object.keys(project.affiliations).length" class="flex items-center gap-1">
<lf-contributor-details-projects-affiliation :project="project" />
</div>
<div v-else-if="hasPermission(LfPermission.memberEdit)">
<lf-button type="primary-link" size="small" class="!text-primary-300 hover:!text-primary-600" @click="isAffilationEditOpen = true">
<lf-button
type="primary-link"
size="small"
class="!text-primary-300 hover:!text-primary-600"
@click="isAffilationEditOpen = true"
>
<lf-icon-old name="add-line" />Add affiliation
</lf-button>
</div>
Expand All @@ -93,11 +100,13 @@
<lf-icon-old name="pencil-line" />Edit affiliation
</lf-dropdown-item>
<lf-dropdown-item
@click="setReportDataModal({
contributor: props.contributor,
type: ReportDataType.PROJECT_AFFILIATION,
attribute: project,
})"
@click="
setReportDataModal({
contributor: props.contributor,
type: ReportDataType.PROJECT_AFFILIATION,
attribute: project,
})
"
>
<lf-icon-old name="feedback-line" class="!text-red-500" />Report issue
</lf-dropdown-item>
Expand All @@ -124,11 +133,11 @@
import LfCard from '@/ui-kit/card/Card.vue';
import LfButton from '@/ui-kit/button/Button.vue';
import LfIconOld from '@/ui-kit/icon/IconOld.vue';
import LfIcon from '@/ui-kit/icon/Icon.vue';
import { computed, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Contributor, ContributorAffiliation } from '@/modules/contributor/types/Contributor';
import LfContributorEditAffilations
from '@/modules/contributor/components/edit/affilations/contributor-affilations-edit.vue';
import LfContributorEditAffilations from '@/modules/contributor/components/edit/affilations/contributor-affilations-edit.vue';
import LfTable from '@/ui-kit/table/Table.vue';
import LfDropdown from '@/ui-kit/dropdown/Dropdown.vue';
import LfTableCell from '@/ui-kit/table/TableCell.vue';
Expand Down Expand Up @@ -156,7 +165,8 @@ const { setReportDataModal } = useSharedStore();
const showMore = ref<boolean>(false);
const isAffilationEditOpen = ref<boolean>(false);

const getAffiliations = (projectId: string) => (props.contributor.affiliations || []).filter((affiliation) => affiliation.segmentId === projectId)
const getAffiliations = (projectId: string) => (props.contributor.affiliations || [])
.filter((affiliation) => affiliation.segmentId === projectId)
.reduce((obj: Record<string, ContributorAffiliation[]>, aff: ContributorAffiliation) => {
if (!obj[aff.organizationId]) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
<template #trigger>
<div class="flex items-center gap-1 py-2">
<p class="text-small">
<span class="font-semibold">
Sort:
</span>
<span class="font-semibold"> Sort: </span>
{{ sorters[sort] }}
</p>
<lf-icon-old name="arrow-down-s-line" :size="16" />
Expand All @@ -37,12 +35,7 @@
@click="onSortChange(key)"
>
<span>{{ label }}</span>
<lf-icon-old
v-if="sort === key"
name="check-line"
:size="16"
class="text-primary-500"
/>
<lf-icon-old v-if="sort === key" name="check-line" :size="16" class="text-primary-500" />
</lf-dropdown-item>
</lf-dropdown>
</div>
Expand Down Expand Up @@ -71,11 +64,7 @@
class="border-2 rounded-full p-0.5"
:class="isNew(contributor) ? 'border-primary-500' : 'border-transparent'"
>
<lf-avatar
:src="avatar(contributor)"
:name="contributor.displayName"
:size="32"
/>
<lf-avatar :src="avatar(contributor)" :name="contributor.displayName" :size="32" />
</div>
<p class="text-medium font-semibold text-black group-hover:text-primary-500 transition">
{{ contributor.displayName }}
Expand Down Expand Up @@ -117,11 +106,7 @@
</div>
</template>
</app-identities-horizontal-list-members>
<lf-tooltip
v-else
placement="top-end"
content="This person's data is not shown because of the GDPR."
>
<lf-tooltip v-else placement="top-end" content="This person's data is not shown because of the GDPR.">
<div class="h-6 w-21 rounded-md bg-gray-200" />
</lf-tooltip>
</div>
Expand All @@ -139,18 +124,13 @@
</p>
</div>
<div
v-if="pagination.total > (pagination.page * pagination.perPage)"
v-if="pagination.total > pagination.page * pagination.perPage"
class="pt-10 pb-6 gap-4 flex justify-center items-center"
>
<p class="text-small text-gray-400">
{{ contributors.length }} of {{ totalContacts }} people
</p>
<lf-button
type="primary-ghost"
loading-text="Loading people..."
:loading="loading"
@click="loadMore"
>
<lf-button type="primary-ghost" loading-text="Loading people..." :loading="loading" @click="loadMore">
Load more
</lf-button>
</div>
Expand All @@ -176,8 +156,7 @@ import LfDropdown from '@/ui-kit/dropdown/Dropdown.vue';
import LfDropdownItem from '@/ui-kit/dropdown/DropdownItem.vue';
import { useLfSegmentsStore } from '@/modules/lf/segments/store';
import LfButton from '@/ui-kit/button/Button.vue';
import AppIdentitiesHorizontalListMembers
from '@/shared/modules/identities/components/identities-horizontal-list-members.vue';
import AppIdentitiesHorizontalListMembers from '@/shared/modules/identities/components/identities-horizontal-list-members.vue';
import pluralize from 'pluralize';
import LfTooltip from '@/ui-kit/tooltip/Tooltip.vue';
import LfSpinner from '@/ui-kit/spinner/Spinner.vue';
Expand All @@ -202,6 +181,7 @@ const totalContacts = ref<number>(0);
const loading = ref<boolean>(false);

const savedBody = ref<any>({});
const searchStr = ref<string>('');

const {
avatar, isNew, identities, isMasked,
Expand Down Expand Up @@ -246,21 +226,18 @@ const doGetMembersCount = () => {
segments: selectedProjectGroup.value?.id ? [selectedProjectGroup.value?.id] : props.organization.segments,
},
true,
)
.then(({ count }) => {
totalContacts.value = count;
});
).then(({ count }) => {
totalContacts.value = count;
});
};

const fetch = () => {
loading.value = true;
MemberService.listMembers({
filter: {
and: [
orgFilter,
savedBody.value,
],
and: [orgFilter, savedBody.value],
},
search: searchStr.value,
Comment on lines 237 to +240
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling in fetch function

The fetch function resets data on error but doesn't provide user feedback about the failure.

Consider adding error handling:

 filter: {
   and: [orgFilter, savedBody.value],
 },
 search: searchStr.value,
+}).catch((error) => {
+  contributors.value = [];
+  pagination.value.total = 0;
+  // Add error notification
+  console.error('Failed to fetch contributors:', error);
+  // If you have a notification system
+  // notify.error('Failed to load contributors. Please try again.');

Committable suggestion skipped: line range outside the PR's diff.

offset: (pagination.value.page - 1) * pagination.value.perPage,
limit: pagination.value.perPage,
orderBy: sort.value,
Expand All @@ -284,15 +261,16 @@ const fetch = () => {
};

const loadMore = () => {
if (pagination.value.total <= (pagination.value.page * pagination.value.perPage)) {
if (pagination.value.total <= pagination.value.page * pagination.value.perPage) {
return;
}
pagination.value.page += 1;
fetch();
};

const onFilterChange = (filterQuery: FilterQuery) => {
savedBody.value = filterQuery.filter;
savedBody.value = filterQuery.body;
searchStr.value = filterQuery.search;
pagination.value.page = 1;
pagination.value.total = 0;
fetch();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,7 @@
<p class="text-tiny text-secondary-300 mb-2">
Community size
</p>
<lf-loading
v-if="loadingMemberCount"
:count="1"
height="1rem"
width="4rem"
class="rounded"
/>
<lf-loading v-if="loadingMemberCount" :count="1" height="1rem" width="4rem" class="rounded" />
<p v-else class="text-small text-gray-600">
{{ pluralize('person', memberCount || 0, true) }}
</p>
Expand All @@ -33,23 +27,26 @@
# of activities
</p>
<lf-loading
v-if="loadingActivityCount || props.organization.activitySycning?.state === MergeActionState.IN_PROGRESS"
v-if="props.organization.activitySycning?.state === MergeActionState.IN_PROGRESS"
:count="1"
height="1rem"
width="4rem"
class="rounded"
/>
<p v-else class="text-small text-gray-600">
{{ activityCount && formatNumber(activityCount) || '-' }}
{{ (activityCount && formatNumber(activityCount)) || '-' }}
</p>
</article>
<article class="px-4 h-full w-1/2 xl:w-1/3 xl:border-l border-gray-200">
<p class="text-tiny text-secondary-300 mb-2">
Joined date
</p>
<p class="text-small text-gray-600">
{{ moment(props.organization.joinedAt).isAfter(moment('1970-01-01', 'year'))
? moment(props.organization.joinedAt).format('MMM DD, YYYY') : '-' }}
{{
moment(props.organization.joinedAt).isAfter(moment('1970-01-01', 'year'))
? moment(props.organization.joinedAt).format('MMM DD, YYYY')
: '-'
}}
</p>
</article>
</div>
Expand All @@ -63,13 +60,14 @@ import moment from 'moment';
import { formatNumber } from '@/utils/number';
import { Organization } from '@/modules/organization/types/Organization';
import pluralize from 'pluralize';
import { computed, onMounted, ref } from 'vue';
import {
computed, onMounted, ref,
} from 'vue';
import { MemberService } from '@/modules/member/member-service';
import { MergeActionState } from '@/shared/modules/merge/types/MemberActions';
import LfLoading from '@/ui-kit/loading/Loading.vue';
import { useLfSegmentsStore } from '@/modules/lf/segments/store';
import { storeToRefs } from 'pinia';
import { ActivityService } from '@/modules/activity/activity-service';
import LfOrganizationDetailsCommunityProjectSelect
from '@/modules/organization/components/details/overview/community/organization-details-community-project-select.vue';

Expand All @@ -82,8 +80,7 @@ const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore());
const selectedSegment = ref<string>(selectedProjectGroup.value?.id || '');
const memberCount = ref<number>(0);
const loadingMemberCount = ref<boolean>(true);
const activityCount = ref<number>(0);
const loadingActivityCount = ref<boolean>(true);
const activityCount = computed<number>(() => props.organization.activityCount);

const doGetMembersCount = () => {
loadingMemberCount.value = true;
Expand All @@ -104,35 +101,10 @@ const doGetMembersCount = () => {
});
};

const doGetActivityCount = () => {
loadingActivityCount.value = true;
ActivityService.query(
{
filter: {
and: [
{ organizationId: { in: [props.organization.id] } },
],
},
limit: 1,
offset: 0,
orderBy: 'timestamp_DESC',
segments: selectedSegment.value ? [selectedSegment.value] : props.organization.segments,
},
true,
)
.then((data) => {
activityCount.value = data.count;
})
.finally(() => {
loadingActivityCount.value = false;
});
};

const hasSegments = computed(() => selectedProjectGroup.value?.id);

const loadData = () => {
doGetMembersCount();
doGetActivityCount();
};

onMounted(() => {
Expand Down
Loading
Loading