Skip to content

Commit 2619cee

Browse files
authored
Implement sorting functionality for project tables (#2224)
Fixes OPS-4153 <img width="2742" height="568" alt="image" src="https://github.com/user-attachments/assets/24d8a32c-7e70-4a76-8880-2ff06538a72d" /> ---------
1 parent 8512f8a commit 2619cee

22 files changed

Lines changed: 687 additions & 29 deletions

File tree

packages/react-ui/src/app/features/connections/components/connection-table.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { PermissionGuard } from '@/app/common/components/permission-guard';
22
import { appConnectionsApi } from '@/app/features/connections/lib/app-connections-api';
33
import { handleMutationError } from '@/app/interceptors/interceptor-utils';
4+
import { isSortDirection } from '@/app/lib/sort-direction';
45
import { formatUtils } from '@/app/lib/utils';
56
import {
67
BlockIcon,
@@ -16,6 +17,7 @@ import {
1617
} from '@openops/components/ui';
1718
import {
1819
AppConnection,
20+
AppConnectionSortBy,
1921
AppConnectionStatus,
2022
MinimalFlow,
2123
Permission,
@@ -34,6 +36,15 @@ import { useConnectionsContext } from './connections-context';
3436
import { DeleteConnectionDialog } from './delete-connection-dialog';
3537
import { EditConnectionDialog } from './edit-connection-dialog';
3638

39+
const isAppConnectionSortBy = (
40+
sortBy?: string,
41+
): sortBy is AppConnectionSortBy => {
42+
return (
43+
!!sortBy &&
44+
Object.values(AppConnectionSortBy).includes(sortBy as AppConnectionSortBy)
45+
);
46+
};
47+
3748
type BlockIconWithBlockNameProps = {
3849
authProviderKey: string;
3950
};
@@ -160,6 +171,7 @@ const columns: (
160171
return [
161172
{
162173
accessorKey: 'authProviderKey',
174+
enableSorting: false,
163175
header: ({ column }) => (
164176
<DataTableColumnHeader column={column} title={t('App')} />
165177
),
@@ -230,6 +242,7 @@ const columns: (
230242
},
231243
{
232244
accessorKey: 'actions',
245+
enableSorting: false,
233246
header: ({ column }) => (
234247
<DataTableColumnHeader column={column} title="" />
235248
),
@@ -264,6 +277,12 @@ const fetchData = async (
264277
cursor: pagination.cursor,
265278
limit: pagination.limit ?? 10,
266279
status: params.status,
280+
sortBy: isAppConnectionSortBy(pagination.sortBy)
281+
? pagination.sortBy
282+
: undefined,
283+
sortDirection: isSortDirection(pagination.sortDirection)
284+
? pagination.sortDirection
285+
: undefined,
267286
});
268287
};
269288

@@ -278,6 +297,7 @@ function AppConnectionsTable() {
278297
fetchData={fetchData}
279298
refresh={refresh}
280299
filters={filters}
300+
enableSorting={true}
281301
/>
282302
</div>
283303
</div>

packages/react-ui/src/app/features/flow-runs/hooks/useRunsTableColumns.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ export const useRunsTableColumns = ({
137137
},
138138
{
139139
accessorKey: 'duration',
140+
enableSorting: false,
140141
header: ({ column }) => (
141142
<DataTableColumnHeader column={column} title={t('Duration')} />
142143
),
@@ -151,6 +152,7 @@ export const useRunsTableColumns = ({
151152
},
152153
{
153154
accessorKey: 'actions',
155+
enableSorting: false,
154156
header: () => null,
155157
cell: ({ row }) => {
156158
const isFailed = isFailedState(row.original.status);

packages/react-ui/src/app/features/flows/flows-columns.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const nameColumn: FlowColumnDef = {
3434

3535
export const integrationsColumn: FlowColumnDef = {
3636
accessorKey: 'integrations',
37+
enableSorting: false,
3738
header: ({ column }) => (
3839
<DataTableColumnHeader column={column} title={t('Integrations')} />
3940
),
@@ -98,6 +99,7 @@ export const updatedColumn: FlowColumnDef = {
9899

99100
export const statusColumn: FlowColumnDef = {
100101
accessorKey: 'status',
102+
enableSorting: false,
101103
header: ({ column }) => (
102104
<DataTableColumnHeader column={column} title={t('Status')} />
103105
),
@@ -118,6 +120,7 @@ export const statusColumn: FlowColumnDef = {
118120

119121
export const validColumn: FlowColumnDef = {
120122
accessorKey: 'valid',
123+
enableSorting: false,
121124
header: ({ column }) => <DataTableColumnHeader column={column} title="" />,
122125
cell: ({ row }) => {
123126
const valid = row.original.version.valid;
@@ -154,6 +157,7 @@ export const createActionsColumn = (
154157
onTableRefresh: () => void,
155158
): FlowColumnDef => ({
156159
accessorKey: 'actions',
160+
enableSorting: false,
157161
header: ({ column }) => <DataTableColumnHeader column={column} title="" />,
158162
cell: ({ row }) => {
159163
const flow = row.original;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { SortDirection } from '@openops/shared';
2+
3+
export function isSortDirection(
4+
sortDirection?: string,
5+
): sortDirection is SortDirection {
6+
return (
7+
!!sortDirection &&
8+
Object.values(SortDirection).includes(sortDirection as SortDirection)
9+
);
10+
}

packages/react-ui/src/app/routes/flows/index.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,17 @@ import {
1717
import { flowsApi } from '@/app/features/flows/lib/flows-api';
1818
import { FlowsFolderBreadcrumbsManager } from '@/app/features/folders/component/folder-breadcrumbs-manager';
1919
import { FLOWS_TABLE_FILTERS } from '@/app/features/folders/lib/flows-table-filters';
20-
import { FlowStatus, FlowVersionState, Permission } from '@openops/shared';
20+
import { isSortDirection } from '@/app/lib/sort-direction';
21+
import {
22+
FlowSortBy,
23+
FlowStatus,
24+
FlowVersionState,
25+
Permission,
26+
} from '@openops/shared';
27+
28+
const isFlowSortBy = (sortBy?: string): sortBy is FlowSortBy => {
29+
return !!sortBy && Object.values(FlowSortBy).includes(sortBy as FlowSortBy);
30+
};
2131

2232
const FlowsPage = () => {
2333
useDefaultSidebarState('expanded');
@@ -43,6 +53,10 @@ const FlowsPage = () => {
4353
return flowsApi.list({
4454
cursor: pagination.cursor,
4555
limit: pagination.limit ?? 10,
56+
sortBy: isFlowSortBy(pagination.sortBy) ? pagination.sortBy : undefined,
57+
sortDirection: isSortDirection(pagination.sortDirection)
58+
? pagination.sortDirection
59+
: undefined,
4660
status: params.status,
4761
versionState: params.versionState,
4862
name: params.name,
@@ -75,6 +89,7 @@ const FlowsPage = () => {
7589
columns={columns}
7690
fetchData={fetchData}
7791
filters={FLOWS_TABLE_FILTERS}
92+
enableSorting={true}
7893
columnVisibility={columnVisibility}
7994
navigationExcludedColumns={['status', 'actions']}
8095
refresh={tableRefresh}

packages/react-ui/src/app/routes/runs/index.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { DataTable, PaginationParams } from '@openops/components/ui';
2-
import { FlowRunStatus, FlowRunTriggerSource } from '@openops/shared';
2+
import {
3+
FlowRunSortBy,
4+
FlowRunStatus,
5+
FlowRunTriggerSource,
6+
} from '@openops/shared';
37
import { t } from 'i18next';
48
import { CheckIcon } from 'lucide-react';
59
import { useEffect, useMemo, useState } from 'react';
@@ -11,8 +15,22 @@ import { useRunsTableColumns } from '@/app/features/flow-runs/hooks/useRunsTable
1115
import { flowRunUtils } from '@/app/features/flow-runs/lib/flow-run-utils';
1216
import { flowRunsApi } from '@/app/features/flow-runs/lib/flow-runs-api';
1317
import { flowsHooks } from '@/app/features/flows/lib/flows-hooks';
18+
import { isSortDirection } from '@/app/lib/sort-direction';
1419
import { formatUtils } from '@/app/lib/utils';
1520

21+
const isFlowRunSortBy = (sortBy?: string): sortBy is FlowRunSortBy => {
22+
return (
23+
!!sortBy && Object.values(FlowRunSortBy).includes(sortBy as FlowRunSortBy)
24+
);
25+
};
26+
27+
const toFlowRunSortBy = (sortBy?: string): FlowRunSortBy | undefined => {
28+
if (sortBy === 'flowId') {
29+
return FlowRunSortBy.FLOW_NAME;
30+
}
31+
return isFlowRunSortBy(sortBy) ? sortBy : undefined;
32+
};
33+
1634
const fetchData = async (
1735
params: {
1836
flowId: string[];
@@ -31,6 +49,10 @@ const fetchData = async (
3149
limit: pagination.limit ?? 10,
3250
createdAfter: pagination.createdAfter,
3351
createdBefore: pagination.createdBefore,
52+
sortBy: toFlowRunSortBy(pagination.sortBy),
53+
sortDirection: isSortDirection(pagination.sortDirection)
54+
? pagination.sortDirection
55+
: undefined,
3456
});
3557
};
3658

@@ -110,6 +132,7 @@ const FlowRunsPage = () => {
110132
<DataTable
111133
columns={columns}
112134
fetchData={fetchData}
135+
enableSorting={true}
113136
navigationExcludedColumns={['actions']}
114137
filters={filters}
115138
refresh={refresh}

packages/server/api/src/app/app-connection/app-connection-service/app-connection-service.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import {
88
AppConnection,
99
AppConnectionId,
10+
AppConnectionSortBy,
1011
AppConnectionStatus,
1112
AppConnectionType,
1213
AppConnectionValue,
@@ -21,6 +22,7 @@ import {
2122
PatchAppConnectionRequestBody as PatchAppConnectionRequestBodySchema,
2223
ProjectId,
2324
SeekPage,
25+
SortDirection,
2426
UpsertAppConnectionRequestBody,
2527
UpsertAppConnectionRequestBody as UpsertAppConnectionRequestBodySchema,
2628
UserId,
@@ -48,6 +50,8 @@ import { oauth2Util } from './oauth2/oauth2-util';
4850
import { engineValidateAuth } from './validate-auth';
4951

5052
const repo = repoFactory(AppConnectionEntity);
53+
const DEFAULT_APP_CONNECTION_SORT_BY = AppConnectionSortBy.UPDATED;
54+
const DEFAULT_APP_CONNECTION_SORT_DIRECTION = SortDirection.DESC;
5155

5256
export const appConnectionService = {
5357
async upsert(params: UpsertParams): Promise<AppConnection> {
@@ -232,20 +236,26 @@ export const appConnectionService = {
232236
limit,
233237
connectionsIds,
234238
authProviders,
239+
sortBy,
240+
sortDirection,
235241
}: ListParams): Promise<SeekPage<AppConnection>> {
242+
const sortingConfig = resolveAppConnectionSorting({
243+
sortBy,
244+
sortDirection,
245+
});
236246
const decodedCursor = paginationHelper.decodeCursor(cursorRequest);
237247

238248
const paginator = buildPaginator({
239249
entity: AppConnectionEntity,
240250
query: {
241251
limit,
242-
order: 'DESC',
252+
order: sortingConfig.order,
243253
afterCursor: decodedCursor.nextCursor,
244254
beforeCursor: decodedCursor.previousCursor,
245255
},
246256
customPaginationColumn: {
247-
columnPath: 'updated',
248-
columnName: 'app_connection.updated',
257+
columnPath: sortingConfig.columnPath,
258+
columnName: sortingConfig.columnName,
249259
},
250260
});
251261

@@ -547,6 +557,8 @@ type ListParams = {
547557
status: AppConnectionStatus[] | undefined;
548558
limit: number;
549559
authProviders: string[] | undefined;
560+
sortBy?: AppConnectionSortBy;
561+
sortDirection?: SortDirection;
550562
};
551563

552564
type CountByProjectParams = {
@@ -557,3 +569,42 @@ type ValidateConnectionValueParams = {
557569
connection: UpsertAppConnectionRequestBody;
558570
projectId: ProjectId;
559571
};
572+
573+
function resolveAppConnectionSorting({
574+
sortBy,
575+
sortDirection,
576+
}: {
577+
sortBy?: AppConnectionSortBy;
578+
sortDirection?: SortDirection;
579+
}): {
580+
columnPath: string;
581+
columnName: string;
582+
order: 'ASC' | 'DESC';
583+
} {
584+
const resolvedSortBy = sortBy ?? DEFAULT_APP_CONNECTION_SORT_BY;
585+
const resolvedSortDirection =
586+
sortDirection ?? DEFAULT_APP_CONNECTION_SORT_DIRECTION;
587+
588+
const sortByToColumnMap: Record<
589+
AppConnectionSortBy,
590+
{ columnPath: string; columnName: string }
591+
> = {
592+
[AppConnectionSortBy.NAME]: {
593+
columnPath: 'name',
594+
columnName: 'app_connection.name',
595+
},
596+
[AppConnectionSortBy.CREATED]: {
597+
columnPath: 'created',
598+
columnName: 'app_connection.created',
599+
},
600+
[AppConnectionSortBy.UPDATED]: {
601+
columnPath: 'updated',
602+
columnName: 'app_connection.updated',
603+
},
604+
};
605+
606+
return {
607+
...sortByToColumnMap[resolvedSortBy],
608+
order: resolvedSortDirection === SortDirection.ASC ? 'ASC' : 'DESC',
609+
};
610+
}

packages/server/api/src/app/app-connection/app-connection.controller.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ export const appConnectionController: FastifyPluginCallbackTypebox = (
7979
cursorRequest: cursor ?? null,
8080
limit: limit ?? DEFAULT_PAGE_SIZE,
8181
authProviders,
82+
sortBy: request.query.sortBy,
83+
sortDirection: request.query.sortDirection,
8284
});
8385

8486
return {

packages/server/api/src/app/flows/flow-run/flow-run-controller.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export const flowRunController: FastifyPluginCallbackTypebox = (
4747
limit: Number(request.query.limit ?? DEFAULT_PAGING_LIMIT),
4848
createdAfter: request.query.createdAfter,
4949
createdBefore: request.query.createdBefore,
50+
sortBy: request.query.sortBy,
51+
sortDirection: request.query.sortDirection,
5052
});
5153
});
5254

0 commit comments

Comments
 (0)