diff --git a/backend/src/database/repositories/githubReposRepository.ts b/backend/src/database/repositories/githubReposRepository.ts index 2c133aaa6b..4400951ae5 100644 --- a/backend/src/database/repositories/githubReposRepository.ts +++ b/backend/src/database/repositories/githubReposRepository.ts @@ -82,4 +82,31 @@ export default class GithubReposRepository { return results } + + static async hasMappedRepos(segmentId: string, options: IRepositoryOptions) { + const transaction = SequelizeRepository.getTransaction(options) + const tenantId = options.currentTenant.id + + const result = await options.database.sequelize.query( + ` + SELECT EXISTS ( + SELECT 1 + FROM "githubRepos" r + WHERE r."segmentId" = :segmentId + AND r."tenantId" = :tenantId + LIMIT 1 + ) as has_repos + `, + { + replacements: { + segmentId, + tenantId, + }, + type: QueryTypes.SELECT, + transaction, + }, + ) + + return result[0].has_repos + } } diff --git a/backend/src/database/repositories/segmentRepository.ts b/backend/src/database/repositories/segmentRepository.ts index cb424a467f..97369edbee 100644 --- a/backend/src/database/repositories/segmentRepository.ts +++ b/backend/src/database/repositories/segmentRepository.ts @@ -706,17 +706,48 @@ class SegmentRepository extends RepositoryBase< const subprojects = projects.map((p) => p.subprojects).flat() const integrationsBySegments = await this.queryIntegrationsForSubprojects(subprojects) + const mappedGithubReposBySegments = ( + await Promise.all( + subprojects.map(async (s) => ({ + segmentId: s.id, + hasMappedRepo: await this.hasMappedRepos(s.id), + })), + ) + ).reduce((acc, { segmentId, hasMappedRepo }) => { + if (hasMappedRepo) { + acc[segmentId] = true + } + return acc + }, {}) const count = projects.length > 0 ? Number.parseInt(projects[0].totalCount, 10) : 0 const rows = projects.map((i) => removeFieldsFromObject(i, 'totalCount')) // assign integrations to subprojects - rows.forEach((row) => { - row.subprojects.forEach((subproject) => { - subproject.integrations = integrationsBySegments[subproject.id] || [] - }) - }) + await Promise.all( + rows.map(async (row) => { + await Promise.all( + row.subprojects.map(async (subproject) => { + const integrations = integrationsBySegments[subproject.id] || [] + const githubIntegration = integrations.find((i) => i.platform === 'github') + + if (githubIntegration) { + githubIntegration.type = 'primary' + } else if (mappedGithubReposBySegments[subproject.id]) { + integrations.push({ + platform: 'github', + segmentId: subproject.id, + type: 'mapped', + mappedWith: await this.mappedWith(subproject.id), + }) + } + + subproject.integrations = integrations + }), + ) + }), + ) return { count, rows, limit: criteria.limit, offset: criteria.offset } } @@ -893,6 +924,65 @@ class SegmentRepository extends RepositoryBase< return segments.map((i: any) => i.id) } + + async hasMappedRepos(segmentId: string) { + const transaction = SequelizeRepository.getTransaction(this.options) + const tenantId = this.options.currentTenant.id + + const result = await this.options.database.sequelize.query( + ` + SELECT EXISTS ( + SELECT 1 + FROM "githubRepos" r + LEFT JOIN "integrations" i ON r."integrationId" = i.id + WHERE r."segmentId" = :segmentId + AND r."tenantId" = :tenantId + AND (i.id IS NULL OR i."segmentId" != :segmentId) + LIMIT 1 + ) as has_repos + `, + { + replacements: { + segmentId, + tenantId, + }, + type: QueryTypes.SELECT, + transaction, + }, + ) + + return !!result[0].has_repos + } + + async mappedWith(segmentId: string) { + const transaction = SequelizeRepository.getTransaction(this.options) + const tenantId = this.options.currentTenant.id + + const result = await this.options.database.sequelize.query( + ` + select + s.name as segment_name + from + "githubRepos" r + left join "integrations" i on r."integrationId" = i.id + left join "segments" s on i."segmentId" = s.id + where r."segmentId" = :segmentId + and r."tenantId" = :tenantId + and (i.id is null or i."segmentId" != :segmentId) + limit 1 + `, + { + replacements: { + segmentId, + tenantId, + }, + type: QueryTypes.SELECT, + transaction, + }, + ) + + return result[0].segment_name as string + } } export default SegmentRepository diff --git a/frontend/public/icons/crowd-icons.svg b/frontend/public/icons/crowd-icons.svg index 979c7dc33b..4913770307 100644 --- a/frontend/public/icons/crowd-icons.svg +++ b/frontend/public/icons/crowd-icons.svg @@ -22,28 +22,28 @@ + d="M13.5447 1.81368C12.5249 1.34576 11.4313 1.00101 10.2879 0.803563C10.2671 0.799752 10.2463 0.809275 10.2356 0.828322C10.0949 1.07847 9.93915 1.4048 9.83006 1.66129C8.60027 1.47718 7.37679 1.47718 6.17221 1.66129C6.0631 1.3991 5.90166 1.07847 5.76038 0.828322C5.74966 0.809911 5.72886 0.800387 5.70803 0.803563C4.56527 1.00038 3.47171 1.34512 2.45129 1.81368C2.44246 1.81748 2.43488 1.82384 2.42986 1.83209C0.355594 4.93099 -0.212633 7.95373 0.0661201 10.939C0.0673814 10.9536 0.0755799 10.9676 0.086932 10.9764C1.45547 11.9815 2.78114 12.5916 4.08219 12.996C4.10301 13.0024 4.12507 12.9948 4.13832 12.9776C4.44608 12.5573 4.72043 12.1142 4.95565 11.6482C4.96953 11.6209 4.95628 11.5885 4.92791 11.5777C4.49275 11.4126 4.0784 11.2114 3.67982 10.9828C3.64829 10.9644 3.64577 10.9193 3.67477 10.8977C3.75865 10.8349 3.84255 10.7695 3.92264 10.7034C3.93713 10.6914 3.95732 10.6888 3.97435 10.6964C6.59286 11.892 9.4277 11.892 12.0153 10.6964C12.0323 10.6882 12.0525 10.6907 12.0677 10.7028C12.1478 10.7688 12.2316 10.8349 12.3161 10.8977C12.3451 10.9193 12.3433 10.9644 12.3117 10.9828C11.9131 11.2158 11.4988 11.4126 11.063 11.5771C11.0346 11.5879 11.022 11.6209 11.0359 11.6482C11.2762 12.1135 11.5505 12.5567 11.8526 12.977C11.8652 12.9948 11.8879 13.0024 11.9087 12.996C13.2161 12.5916 14.5417 11.9815 15.9103 10.9764C15.9223 10.9676 15.9298 10.9542 15.9311 10.9396C16.2647 7.48834 15.3723 4.49039 13.5655 1.83272C13.5611 1.82384 13.5535 1.81748 13.5447 1.81368ZM5.34668 9.12128C4.55833 9.12128 3.90876 8.39752 3.90876 7.50866C3.90876 6.61981 4.54574 5.89604 5.34668 5.89604C6.15392 5.89604 6.79721 6.62616 6.78459 7.50866C6.78459 8.39752 6.14761 9.12128 5.34668 9.12128ZM10.6632 9.12128C9.87484 9.12128 9.22526 8.39752 9.22526 7.50866C9.22526 6.61981 9.86222 5.89604 10.6632 5.89604C11.4704 5.89604 12.1137 6.62616 12.1011 7.50866C12.1011 8.39752 11.4704 9.12128 10.6632 9.12128Z" fill=" #7289da" /> - + - + - + - + @@ -52,7 +52,7 @@ - + @@ -124,7 +124,7 @@ + d="M4.29 6.29607C4.15143 6.1925 4.0125 6.14071 3.87393 6.14071H3.25107V9.87179H3.87429C4.01286 9.87179 4.15179 9.82 4.29036 9.71643C4.42893 9.61286 4.49821 9.4575 4.49821 9.25V6.7625C4.49786 6.55536 4.42821 6.39964 4.29 6.29607ZM14.4321 0H1.56786C0.703571 0 0.00214286 0.699643 0 1.56429V14.4357C0.00214286 15.3004 0.703571 16 1.56786 16H14.4321C15.2968 16 15.9979 15.3004 16 14.4357V1.56429C15.9979 0.699643 15.2964 0 14.4321 0ZM5.50714 9.25679C5.50714 9.92857 5.0925 10.9464 3.78 10.9443H2.12286V5.035H3.815C5.08071 5.035 5.50643 6.05143 5.50679 6.72357L5.50714 9.25679ZM9.10286 6.09036H7.2V7.4625H8.36321V8.51857H7.2V9.89036H9.10321V10.9464H6.8825C6.48393 10.9568 6.1525 10.6418 6.1425 10.2432V5.775C6.13286 5.37679 6.44821 5.04607 6.84643 5.03607H9.10321L9.10286 6.09036ZM12.8043 10.2079C12.3329 11.3061 11.4882 11.0875 11.11 10.2079L9.73393 5.03643H10.8971L11.9582 9.09786L13.0143 5.03643H14.1779L12.8043 10.2079Z" fill="black" /> @@ -214,7 +214,7 @@ - @@ -222,7 +222,7 @@ - + + d="M6.167 8a.831.831 0 0 0-.83.83c0 .459.372.84.83.831a.831.831 0 0 0 0-1.661zm1.843 3.647c.315 0 1.403-.038 1.976-.611a.232.232 0 0 0 0-.306.213.213 0 0 0-.306 0c-.353.363-1.126.487-1.67.487-.545 0-1.308-.124-1.671-.487a.213.213 0 0 0-.306 0 .213.213 0 0 0 0 .306c.564.563 1.652.61 1.977.61zm.992-2.807c0 .458.373.83.831.83.458 0 .83-.381.83-.83a.831.831 0 0 0-1.66 0z" + fill="#FF4500" /> + d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.828-1.165c-.315 0-.602.124-.812.325-.801-.573-1.9-.945-3.121-.993l.534-2.501 1.738.372a.83.83 0 1 0 .83-.869.83.83 0 0 0-.744.468l-1.938-.41a.203.203 0 0 0-.153.028.186.186 0 0 0-.086.134l-.592 2.788c-1.24.038-2.358.41-3.17.992-.21-.2-.496-.324-.81-.324a1.163 1.163 0 0 0-.478 2.224c-.02.115-.029.23-.029.353 0 1.795 2.091 3.256 4.669 3.256 2.577 0 4.668-1.451 4.668-3.256 0-.114-.01-.238-.029-.353.401-.181.688-.592.688-1.069 0-.65-.525-1.165-1.165-1.165z" + fill="#FF4500" /> @@ -243,7 +245,7 @@ xmlns="http://www.w3.org/2000/svg"> + fill="#0A66C2" /> @@ -285,7 +287,7 @@ + fill="#f1502f" /> @@ -299,7 +301,7 @@ + fill="black" /> @@ -314,7 +316,7 @@ - + @@ -346,4 +348,21 @@ + + + + + + + + + + + + + + + + + diff --git a/frontend/src/modules/dashboard/components/dashboard-project-group-drawer.vue b/frontend/src/modules/dashboard/components/dashboard-project-group-drawer.vue index f31a998819..02b869af37 100644 --- a/frontend/src/modules/dashboard/components/dashboard-project-group-drawer.vue +++ b/frontend/src/modules/dashboard/components/dashboard-project-group-drawer.vue @@ -79,12 +79,22 @@ class="flex gap-3 items-center" >
{ }); const isLoadMoreVisible = computed( - () => pagination.value.currentPage * pagination.value.pageSize < pagination.value.count || loading.value, + () => pagination.value.currentPage * pagination.value.pageSize + < pagination.value.count || loading.value, ); const { trackEvent } = useProductTracking(); diff --git a/frontend/src/modules/lf/segments/components/view/lf-projects-table.vue b/frontend/src/modules/lf/segments/components/view/lf-projects-table.vue index e1d0408b7d..e786d323a0 100644 --- a/frontend/src/modules/lf/segments/components/view/lf-projects-table.vue +++ b/frontend/src/modules/lf/segments/components/view/lf-projects-table.vue @@ -16,7 +16,9 @@