Paginate admin panel app registrations list (#22734)

## Context

The `findAllApplicationRegistrations` query on the admin panel Apps page
(`/settings/admin-panel#apps`) loaded every application registration at
once, with search and filtering done client-side.

## Changes

**Server**
- `findAllApplicationRegistrations` now takes `limit` / `offset` /
`searchTerm` / `isPreInstalledOnly` args and returns a
`PaginatedApplicationRegistrations` object (`registrations`,
`totalCount`, `hasMore`), following the same pattern as `getQueueJobs`.
- `ApplicationRegistrationService.findAll` uses `findAndCount` with
`take`/`skip`, and moves the search (name, source package, universal
identifier via `ILIKE`) and the pre-installed filter into the SQL query,
mirroring how `getInstalledWorkspacesGlobal` filters installed
workspaces.

**Frontend**
- `SettingsAdminApps` passes the page, the debounced search term (300ms,
like the installed workspaces table), and the pre-installed toggle as
query variables instead of filtering client-side.
- Adds a Previous / Next pagination footer (25 per page) matching the
queue jobs table, shown only when there is more than one page.
- The "unconfigured first" ordering is kept within each page
(`isConfigured` is a dataloader-resolved field, so it can't be sorted in
SQL).

## Notes
- Regenerated `generated-admin/graphql.ts` follows in a subsequent
commit.

---
_Generated by [Claude
Code](https://claude.ai/code/session_015erumgPozkbNA3zPeKrrFW)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22734?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->

---------

Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
martmull
2026-07-13 14:07:11 +02:00
committed by GitHub
parent b06f3c7f8f
commit 7381038452
12 changed files with 353 additions and 98 deletions
@@ -55,6 +55,7 @@ import { ApplicationRegistrationService } from 'src/engine/core-modules/applicat
import { ApplicationRegistrationInstalledWorkspacesDTO } from 'src/engine/core-modules/application/application-registration/dtos/application-registration-installed-workspaces.dto';
import { ApplicationRegistrationStatsDTO } from 'src/engine/core-modules/application/application-registration/dtos/application-registration-stats.dto';
import { FindApplicationRegistrationInstalledWorkspacesInput } from 'src/engine/core-modules/application/application-registration/dtos/find-application-registration-installed-workspaces.input';
import { PaginatedApplicationRegistrationsDTO } from 'src/engine/core-modules/application/application-registration/dtos/paginated-application-registrations.dto';
import { UpdateApplicationRegistrationInput } from 'src/engine/core-modules/application/application-registration/dtos/update-application-registration.input';
import {
BACKFILL_APPLICATION_INSTALLATION_JOB_NAME,
@@ -105,8 +106,6 @@ import { ModelsDevProviderSuggestionDTO } from './dtos/models-dev-provider-sugge
import { QueueMetricsDataDTO } from './dtos/queue-metrics-data.dto';
import { SetMaintenanceModeInput } from './dtos/set-maintenance-mode.input';
const INSTALLED_WORKSPACES_PAGE_SIZE = 10;
@UsePipes(ResolverValidationPipe)
@AdminResolver()
@UseFilters(
@@ -480,11 +479,23 @@ export class AdminPanelResolver {
}
@UseGuards(AdminPanelGuard)
@Query(() => [ApplicationRegistrationEntity])
async findAllApplicationRegistrations(): Promise<
ApplicationRegistrationEntity[]
> {
return this.applicationRegistrationService.findAll();
@Query(() => PaginatedApplicationRegistrationsDTO)
async findAllApplicationRegistrations(
@Args('limit', { type: () => Int, nullable: true, defaultValue: 25 })
limit: number,
@Args('offset', { type: () => Int, nullable: true, defaultValue: 0 })
offset: number,
@Args('searchTerm', { type: () => String, nullable: true })
searchTerm?: string,
@Args('isPreInstalledOnly', { type: () => Boolean, nullable: true })
isPreInstalledOnly?: boolean,
): Promise<PaginatedApplicationRegistrationsDTO> {
return this.applicationRegistrationService.findAll({
limit,
offset,
searchTerm,
isPreInstalledOnly,
});
}
@UseGuards(AdminPanelGuard)
@@ -838,14 +849,15 @@ export class AdminPanelResolver {
@Args('input')
{
id,
page,
limit,
offset,
searchTerm,
}: FindApplicationRegistrationInstalledWorkspacesInput,
): Promise<ApplicationRegistrationInstalledWorkspacesDTO> {
return this.applicationRegistrationService.getInstalledWorkspacesGlobal(
id,
page ?? 1,
INSTALLED_WORKSPACES_PAGE_SIZE,
limit,
offset,
searchTerm,
);
}