diff --git a/frontend/src/modules/contributor/components/details/contributor-details-activities.vue b/frontend/src/modules/contributor/components/details/contributor-details-activities.vue
index 4401cb601f..ede21994c3 100644
--- a/frontend/src/modules/contributor/components/details/contributor-details-activities.vue
+++ b/frontend/src/modules/contributor/components/details/contributor-details-activities.vue
@@ -1,5 +1,16 @@
+
+
+
+ Syncing activities...
+
+
+ Re-syncing all the activities as this organization was recently merged.
+ This process may take some minutes.
+
+
# of activities
-
+
+
{{ props.contributor.activityCount && formatNumber(props.contributor.activityCount) || '-' }}
@@ -57,6 +64,8 @@ import { formatNumber } from '@/utils/number';
import LfContributorEngagementLevel from '@/modules/contributor/components/shared/contributor-engagement-level.vue';
import LfContributorSentiment from '@/modules/contributor/components/shared/contributor-sentiment.vue';
import { Contributor } from '@/modules/contributor/types/Contributor';
+import LfLoading from '@/ui-kit/loading/Loading.vue';
+import { MergeActionState } from '@/shared/modules/merge/types/MemberActions';
const props = defineProps<{
contributor: Contributor,
diff --git a/frontend/src/modules/contributor/components/shared/contributor-syncing-activities.vue b/frontend/src/modules/contributor/components/shared/contributor-syncing-activities.vue
new file mode 100644
index 0000000000..7b192fac30
--- /dev/null
+++ b/frontend/src/modules/contributor/components/shared/contributor-syncing-activities.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+ Syncing activities...
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/modules/contributor/pages/contributor-details.page.vue b/frontend/src/modules/contributor/pages/contributor-details.page.vue
index 5686ef4831..946678e013 100644
--- a/frontend/src/modules/contributor/pages/contributor-details.page.vue
+++ b/frontend/src/modules/contributor/pages/contributor-details.page.vue
@@ -19,7 +19,8 @@
-
+
+
@@ -100,6 +101,7 @@ import LfContributorDetailsHeader from '@/modules/contributor/components/details
import LfContributorDetailsActions from '@/modules/contributor/components/details/contributor-details-actions.vue';
import LfContributorLastEnrichment from '@/modules/contributor/components/shared/contributor-last-enrichment.vue';
import { useContributorStore } from '@/modules/contributor/store/contributor.store';
+import LfContributorSyncingActivities from '@/modules/contributor/components/shared/contributor-syncing-activities.vue';
const { getMemberCustomAttributes } = useMemberStore();
diff --git a/frontend/src/modules/contributor/store/contributor.actions.ts b/frontend/src/modules/contributor/store/contributor.actions.ts
index ebeaf3ce6c..8e9e8a9f31 100644
--- a/frontend/src/modules/contributor/store/contributor.actions.ts
+++ b/frontend/src/modules/contributor/store/contributor.actions.ts
@@ -2,16 +2,33 @@ import { useLfSegmentsStore } from '@/modules/lf/segments/store';
import { storeToRefs } from 'pinia';
import { ContributorApiService } from '@/modules/contributor/services/contributor.api.service';
import { Contributor } from '@/modules/contributor/types/Contributor';
+import { MergeActionsService } from '@/shared/modules/merge/services/merge-actions.service';
+import { MergeAction } from '@/shared/modules/merge/types/MemberActions';
export default {
getContributor(id: string): Promise
{
const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore());
+
return ContributorApiService.find(id, [selectedProjectGroup.value?.id as string])
.then((contributor) => {
this.contributor = contributor;
+ this.getContributorMergeActions(id);
return Promise.resolve(contributor);
});
},
+ getContributorMergeActions(id: string): Promise {
+ return MergeActionsService.list(id)
+ .then((mergeActions) => {
+ const inProgress = mergeActions.some((action) => action.state === 'in-progress');
+ const error = mergeActions.some((action) => action.state === 'error');
+ this.contributor = {
+ ...this.contributor,
+ // eslint-disable-next-line no-nested-ternary
+ activitySycning: inProgress ? 'in-progress' : (error ? 'error' : ''),
+ };
+ return Promise.resolve(mergeActions);
+ });
+ },
updateContributor(id: string, data: Partial) {
const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore());
return ContributorApiService.update(id, data, [selectedProjectGroup.value?.id as string])
diff --git a/frontend/src/modules/contributor/types/Contributor.ts b/frontend/src/modules/contributor/types/Contributor.ts
index 39e8c08b6b..045ee4b052 100644
--- a/frontend/src/modules/contributor/types/Contributor.ts
+++ b/frontend/src/modules/contributor/types/Contributor.ts
@@ -82,5 +82,6 @@ export interface Contributor {
id: string;
name: string;
activityCount: string;
- }[]
+ }[],
+ activitySycning: string;
}
diff --git a/frontend/src/shared/modules/merge/services/merge-actions.service.ts b/frontend/src/shared/modules/merge/services/merge-actions.service.ts
new file mode 100644
index 0000000000..4f599847ec
--- /dev/null
+++ b/frontend/src/shared/modules/merge/services/merge-actions.service.ts
@@ -0,0 +1,23 @@
+import authAxios from '@/shared/axios/auth-axios';
+import { AuthService } from '@/modules/auth/services/auth.service';
+import { MergeAction } from '@/shared/modules/merge/types/MemberActions';
+
+export class MergeActionsService {
+ static async list(entityId: string, type: string = 'member'): Promise {
+ const tenantId = AuthService.getTenantId();
+
+ const response = await authAxios.get(
+ `/tenant/${tenantId}/mergeActions`,
+ {
+ params: {
+ filter: {
+ entityId,
+ type,
+ },
+ },
+ },
+ );
+
+ return response.data;
+ }
+}
diff --git a/frontend/src/shared/modules/merge/types/MemberActions.ts b/frontend/src/shared/modules/merge/types/MemberActions.ts
new file mode 100644
index 0000000000..a1ee6802b9
--- /dev/null
+++ b/frontend/src/shared/modules/merge/types/MemberActions.ts
@@ -0,0 +1,12 @@
+export enum MergeActionState {
+ IN_PROGRESS = 'in-progress',
+ DONE = 'done',
+ ERROR = 'error',
+}
+
+export interface MergeAction {
+ operationType: string;
+ primaryId: string;
+ secondaryId: string;
+ state: MergeActionState;
+}
diff --git a/frontend/src/ui-kit/index.scss b/frontend/src/ui-kit/index.scss
index d6fd201ba4..1a99d31025 100644
--- a/frontend/src/ui-kit/index.scss
+++ b/frontend/src/ui-kit/index.scss
@@ -6,6 +6,7 @@
@import 'dropdown/dropdown';
@import 'field/field';
@import 'input/input';
+@import 'loading/loading';
@import 'modal/modal';
@import 'field-message/field-message';
@import 'radio/radio';
diff --git a/frontend/src/ui-kit/loading/Loading.stories.ts b/frontend/src/ui-kit/loading/Loading.stories.ts
new file mode 100644
index 0000000000..03824b1838
--- /dev/null
+++ b/frontend/src/ui-kit/loading/Loading.stories.ts
@@ -0,0 +1,34 @@
+import LfLoading from './Loading.vue';
+
+export default {
+ title: 'LinuxFoundation/Loading',
+ component: LfLoading,
+ tags: ['autodocs'],
+ argTypes: {
+ // Propr
+ height: {
+ description: 'Loader height',
+ defaultValue: '100px',
+ control: 'text',
+ },
+ width: {
+ description: 'Loader width',
+ defaultValue: '100%',
+ control: 'text',
+ },
+ count: {
+ description: 'Number of loaders',
+ defaultValue: '1',
+ control: 'number',
+ },
+ },
+};
+
+export const Default = {
+ label: 'Primary',
+ args: {
+ height: '100px',
+ width: '100%',
+ count: 1,
+ },
+};
diff --git a/frontend/src/ui-kit/loading/Loading.vue b/frontend/src/ui-kit/loading/Loading.vue
new file mode 100644
index 0000000000..4f051f16b0
--- /dev/null
+++ b/frontend/src/ui-kit/loading/Loading.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/ui-kit/loading/loading.scss b/frontend/src/ui-kit/loading/loading.scss
new file mode 100644
index 0000000000..c5069bf1b9
--- /dev/null
+++ b/frontend/src/ui-kit/loading/loading.scss
@@ -0,0 +1,36 @@
+/* ---------------------------------- *\
+ Loading
+\* ---------------------------------- */
+
+@keyframes shimmer {
+ 100% {
+ transform: translateX(100%);
+ }
+}
+
+.c-loading {
+ display: inline-block;
+ height: 1em;
+ position: relative;
+ overflow: hidden;
+ background-color: var(--lf-color-secondary-50);
+
+ &:after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ transform: translateX(-100%);
+ background-image: linear-gradient(
+ 90deg,
+ rgba(255, 255, 255, 0) 0%,
+ rgba(255, 255, 255, 0.2) 20.24%,
+ rgba(255, 255, 255, 0.5) 42.57%,
+ rgba(255, 255, 255, 0) 66.35%
+ );
+ animation: shimmer 1.5s infinite;
+ content: '';
+ }
+}
+