Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into crowd-linux
Browse files Browse the repository at this point in the history
  • Loading branch information
joanagmaia committed Sep 7, 2023
2 parents a206887 + f2ea9df commit f8665df
Show file tree
Hide file tree
Showing 30 changed files with 651 additions and 173 deletions.
4 changes: 2 additions & 2 deletions backend/src/database/repositories/organizationRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ class OrganizationRepository {
segment_level as (select case
when "parentSlug" is not null and "grandparentSlug" is not null
then 'child'
when "parentSlug" is null and "grandparentSlug" is not null
when "parentSlug" is not null and "grandparentSlug" is null
then 'parent'
when "parentSlug" is null and "grandparentSlug" is null
then 'grandparent'
Expand All @@ -495,7 +495,7 @@ class OrganizationRepository {
segment_level sl
on
(sl.level = 'child' and s.id = sl.id) or
(sl.level = 'parent' and s."parentSlug" = sl.slug) or
(sl.level = 'parent' and s."parentSlug" = sl.slug and s."grandparentSlug" is not null) or
(sl.level = 'grandparent' and s."grandparentSlug" = sl.slug)`
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 31 additions & 2 deletions frontend/src/integrations/github/components/github-connect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
</template>

<script setup>
import { defineProps, computed } from 'vue';
import { computed } from 'vue';
import config from '@/config';
import ConfirmDialog from '@/shared/dialog/confirm-dialog';
import { useRouter } from 'vue-router';
defineProps({
integration: {
Expand All @@ -13,11 +15,38 @@ defineProps({
},
});
const router = useRouter();
// We have 3 GitHub apps: test, test-local and prod
// Getting the proper URL from config file
const githubConnectUrl = computed(() => config.gitHubInstallationUrl);
const connect = () => {
window.open(githubConnectUrl.value, '_self');
ConfirmDialog({
type: 'notification',
title:
'Are you the admin of your GitHub organization?',
titleClass: 'text-lg pt-2',
message:
`Only GitHub users with admin permissions are able to connect crowd.dev's GitHub integration.
If you are an organization member, you will need an approval from the GitHub workspace admin. <a href="https://docs.crowd.dev/docs/github-integration" target="_blank">Read more</a>`,
icon: 'ri-information-line',
confirmButtonText: 'I\'m the GitHub organization admin',
cancelButtonText: 'Invite organization admin to this workspace',
verticalCancelButtonClass: 'btn btn--md btn--primary w-full',
verticalConfirmButtonClass: 'btn btn--md btn--secondary w-full !mb-2',
vertical: true,
distinguishCancelAndClose: true,
autofocus: false,
messageClass: 'text-xs !leading-5 !mt-1 text-gray-600',
}).then(() => {
window.open(githubConnectUrl.value, '_self');
}).catch((action) => {
if (action === 'cancel') {
router.push({
name: 'settings',
});
}
});
};
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ defineProps({
});
const connectUrl = computed(() => {
const redirectUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}?success=true`;
const redirectUrl = `${window.location.protocol}//${window.location.host}${window.location.pathname}?slack-success=true`;
return `${config.backendUrl}/slack/${
store.getters['auth/currentTenant'].id
Expand Down
113 changes: 74 additions & 39 deletions frontend/src/modules/integration/components/integration-list-item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,60 @@
<div class="s panel" :class="computedClass">
<div class="flex items-center justify-between">
<img :alt="integration.name" :src="integration.image" class="h-6 mb-4" />
<span v-if="isDone" class="badge badge--green">Connected</span>
<div v-else-if="isError" class="text-red-500 flex items-center text-sm">
<i class="ri-error-warning-line mr-1" /> Failed to connect
</div>
<div v-else-if="isNoData" class="text-red-500 flex items-center text-sm">
<i class="ri-error-warning-line mr-1" /> Not receiving activities
</div>
<div
v-else-if="isWaitingForAction"
class="text-yellow-600 flex items-center text-sm"
>
<i class="ri-alert-line mr-1" /> Action required
</div>
<div
v-else-if="isWaitingApproval"
class="text-gray-500 flex items-center text-sm"
>
<i class="ri-time-line mr-1" /> Waiting for approval
</div>
<div
v-else-if="isNeedsToBeReconnected"
class="text-yellow-600 flex items-center text-sm"
>
<i class="ri-alert-line mr-1" /> Needs to be reconnected
</div>
<div v-else-if="isConnected" class="flex items-center">
<div
v-loading="true"
class="app-page-spinner !relative h-4 !w-4 mr-2 !min-h-fit"
/>

<span class="text-xs text-gray-900 mr-2">In progress</span>
<el-tooltip
content="Fetching first activities from an integration might take a few minutes"
placement="top"
>
<i class="ri-question-line text-gray-400" />
</el-tooltip>
<div>
<div class="mb-1 flex justify-end">
<span v-if="isDone" class="badge badge--green">Connected</span>
<div v-else-if="isError" class="text-red-500 flex items-center text-sm">
<i class="ri-error-warning-line mr-1" /> Failed to connect
</div>
<div v-else-if="isNoData" class="text-red-500 flex items-center text-sm">
<i class="ri-error-warning-line mr-1" /> Not receiving activities
</div>
<div
v-else-if="isWaitingForAction"
class="text-yellow-600 flex items-center text-sm"
>
<i class="ri-alert-line mr-1" /> Action required
</div>
<div
v-else-if="isWaitingApproval"
class="text-gray-500 flex items-center text-sm"
>
<i class="ri-time-line mr-1" /> Waiting for approval
</div>
<div
v-else-if="isNeedsToBeReconnected"
class="text-yellow-600 flex items-center text-sm"
>
<i class="ri-alert-line mr-1" /> Needs to be reconnected
</div>
<div v-else-if="isConnected" class="flex items-center">
<div
v-loading="true"
class="app-page-spinner !relative h-4 !w-4 mr-2 !min-h-fit"
/>

<span class="text-xs text-gray-900 mr-2">In progress</span>
<el-tooltip
content="Fetching first activities from an integration might take a few minutes"
placement="top"
>
<i class="ri-question-line text-gray-400" />
</el-tooltip>
</div>
</div>
<div class="h-5 leading-5 text-end">
<el-tooltip
v-if="isDone"
:content="lastSynced.absolute"
placement="top"
>
<span class="text-3xs italic text-gray-500">{{ lastSynced.relative }}</span>
</el-tooltip>
</div>
</div>
</div>

<div>
<div class="flex mb-2">
<span class="block font-semibold">{{ integration.name }}</span>
Expand Down Expand Up @@ -101,11 +115,12 @@

<script setup>
import { useStore } from 'vuex';
import { defineProps, computed, ref } from 'vue';
import { computed, onMounted, ref } from 'vue';
import { FeatureFlag } from '@/featureFlag';
import AppIntegrationConnect from '@/modules/integration/components/integration-connect.vue';
import { isCurrentDateAfterGivenWorkingDays } from '@/utils/date';
import { ERROR_BANNER_WORKING_DAYS_DISPLAY } from '@/modules/integration/integration-store';
import moment from 'moment';
const store = useStore();
const props = defineProps({
Expand All @@ -115,6 +130,21 @@ const props = defineProps({
},
});
onMounted(() => {
moment.updateLocale('en', {
relativeTime: {
s: '1s',
ss: '%ds',
m: '1min',
mm: '%dmin',
h: '1h',
hh: '%dh',
d: '1d',
dd: '%dd',
},
});
});
const computedClass = computed(() => ({
'integration-custom': props.integration.platform === 'custom',
}));
Expand Down Expand Up @@ -146,6 +176,11 @@ const isNeedsToBeReconnected = computed(
() => props.integration.status === 'needs-reconnect',
);
const lastSynced = computed(() => ({
absolute: moment(props.integration.updatedAt).format('MMM DD, YYYY HH:mm'),
relative: `last synced ${moment(props.integration.updatedAt).fromNow()}`,
}));
const loadingDisconnect = ref(false);
const handleDisconnect = async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,6 @@
>Read more</a>
</template>
</app-alert>
<app-alert
v-if="
integrations.slack
&& integrations.slack.status === 'in-progress'
"
>
<template #body>
Add Slack Bot to channels.
<a
href="https://docs.crowd.dev/docs/slack-integration#how-to-install"
class="font-semibold absolute right-0 inset-y-0 flex items-center pr-4 hover:underline"
rel="noopener noreferrer"
target="_blank"
>Read more</a>
</template>
</app-alert>
<app-alert
v-if="
integrations.discord
Expand Down Expand Up @@ -118,6 +102,7 @@
<script>
import { mapGetters } from 'vuex';
import AppLfIntegrationsPageHeader from '@/modules/lf/layout/components/lf-integrations-page-header.vue';
import ConfirmDialog from '@/shared/dialog/confirm-dialog';
import AppIntegrationList from './integration-list.vue';
export default {
Expand All @@ -132,5 +117,45 @@ export default {
integrationsWithNoData: 'integration/withNoData',
}),
},
watch: {
'$route.query.slack-success': {
immediate: true,
handler(value) {
if (value) {
ConfirmDialog({
vertical: true,
type: 'custom',
icon: '<img src="https://cdn-icons-png.flaticon.com/512/3800/3800024.png" class="h-8 w-8" alt="slack logo" />',
title: `<span class="flex items-start gap-1">Connect Slack bot
<span class="text-brand-500 text-3xs leading-3 pt-1 font-normal">Required</span></span>`,
titleClass: 'text-lg',
message: `
<img src="/images/integrations/slack-bot.png" class="mb-6" alt="slack bot installation" />
To fetch data from Slack, you need to install the crowd.dev Slack bot and add it to all channels you want to track. <br><br>
You can either add the Slack bot directly from a channel, or add the app via channel Integrations.`,
confirmButtonText: 'How to connect Slack bot',
showCancelButton: false,
messageClass: 'text-xs !leading-5 !mt-1 text-gray-600',
verticalCustomClass: 'custom-slack-message-box',
closeOnClickModal: false,
hideCloseButton: true,
}).then(() => {
window.open('https://docs.crowd.dev/docs/slack-integration#how-to-install', '_blank');
this.$router.replace({ query: null });
});
}
},
},
},
};
</script>

<style>
.custom-slack-message-box .el-button--primary span::after {
content: "\ecaf";
font-family: "remixicon" !important;
font-size: 18px;
margin-left: 0.5rem;
}
</style>
24 changes: 24 additions & 0 deletions frontend/src/modules/member/config/filters/jobTitle/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig';
import {
StringFilterConfig,
StringFilterOptions,
StringFilterValue,
} from '@/shared/modules/filters/types/filterTypes/StringFilterConfig';
import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType';
import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType';

const jobTitle: StringFilterConfig = {
id: 'jobTitle',
label: 'Job title',
iconClass: 'ri-suitcase-line',
type: FilterConfigType.STRING,
options: {},
itemLabelRenderer(value: StringFilterValue, options: StringFilterOptions): string {
return itemLabelRendererByType[FilterConfigType.STRING]('Job title', value, options);
},
apiFilterRenderer(value: StringFilterValue): any[] {
return apiFilterRendererByType[FilterConfigType.STRING]('attributes.jobTitle.default', value);
},
};

export default jobTitle;
4 changes: 4 additions & 0 deletions frontend/src/modules/member/config/filters/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ import lastActivityDate from './lastActivityDate/config';
import reach from './reach/config';
import projects from './projects/config';
import tags from './tags/config';
import memberName from './memberName/config';
import jobTitle from './jobTitle/config';

export const memberFilters: Record<string, FilterConfig> = {
memberName,
noOfActivities,
noOfOSSContributions,
jobTitle,
activeOn,
activityType,
avgSentiment,
Expand Down
31 changes: 31 additions & 0 deletions frontend/src/modules/member/config/filters/memberName/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig';
import {
StringFilterConfig,
StringFilterOptions,
StringFilterValue,
} from '@/shared/modules/filters/types/filterTypes/StringFilterConfig';
import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType';
import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType';

const memberName: StringFilterConfig = {
id: 'memberName',
label: 'Member name',
iconClass: 'ri-account-circle-line',
type: FilterConfigType.STRING,
options: {},
itemLabelRenderer(
value: StringFilterValue,
options: StringFilterOptions,
): string {
return itemLabelRendererByType[FilterConfigType.STRING](
'Member name',
value,
options,
);
},
apiFilterRenderer(value: StringFilterValue): any[] {
return apiFilterRendererByType[FilterConfigType.STRING]('displayName', value);
},
};

export default memberName;
Loading

0 comments on commit f8665df

Please sign in to comment.