diff --git a/adminforth/modules/restApi.ts b/adminforth/modules/restApi.ts index 81c1f58e0..1c2481bf1 100644 --- a/adminforth/modules/restApi.ts +++ b/adminforth/modules/restApi.ts @@ -2431,6 +2431,45 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { } } }); + server.endpoint({ + method: 'POST', + path: '/next_filtered_record', + handler: async ({ body, adminUser }) => { + const { resourceId, offset, filters, sort } = body; + + const resource = this.adminforth.config.resources.find( + (res) => res.resourceId === resourceId + ); + if (!resource) { + return { error: `Resource ${resourceId} not found` }; + } + + const { allowedActions } = await interpretResource( + adminUser, resource, {}, ActionCheckSource.ShowRequest, this.adminforth + ); + const { allowed, error } = checkAccess(AllowedActionsEnum.show, allowedActions); + if (!allowed) { + return { error }; + } + + const pkColumn = resource.columns.find((col) => col.primaryKey); + + const data = await this.adminforth.connectors[resource.dataSource].getData({ + resource, + filters: { + operator: AdminForthFilterOperators.AND, + subFilters: filters || [], + }, + limit: 1, + offset: offset + 1, + sort: sort || [], + }); + + const record = data.data?.[0]; + return { id: record?.[pkColumn.name] ?? null }; + }, + }); + // setup endpoints for all plugins this.adminforth.activatedPlugins.forEach((plugin) => { plugin.setupEndpoints(server); diff --git a/adminforth/spa/src/components/ListActionsThreeDots.vue b/adminforth/spa/src/components/ListActionsThreeDots.vue index f6205c56c..66ece7fef 100644 --- a/adminforth/spa/src/components/ListActionsThreeDots.vue +++ b/adminforth/spa/src/components/ListActionsThreeDots.vue @@ -23,7 +23,8 @@ params: { resourceId: props.resourceId, primaryKey: record._primaryKeyValue, - } + }, + query: props.rowOffset !== undefined ? { offset: props.rowOffset } : undefined, }" > @@ -116,6 +117,7 @@ const props = defineProps<{ record: any; customActionIconsThreeDotsMenuItems: AdminForthComponentDeclaration[]; resourceId: string; + rowOffset?: number; deleteRecord: (record: any) => void; updateRecords: () => void; startCustomAction: (actionId: string, row: any, extraData?: Record) => void; diff --git a/adminforth/spa/src/components/ResourceListTable.vue b/adminforth/spa/src/components/ResourceListTable.vue index 18fdb04b2..3d4373fd5 100644 --- a/adminforth/spa/src/components/ResourceListTable.vue +++ b/adminforth/spa/src/components/ResourceListTable.vue @@ -143,7 +143,8 @@ params: { resourceId: resource.resourceId, primaryKey: row._primaryKeyValue, - } + }, + query: { offset: rowGlobalOffset(row) }, }" > @@ -238,6 +239,7 @@ :updateRecords="()=>emits('update:records', true)" :deleteRecord="deleteRecord" :resourceId="resource.resourceId" + :rowOffset="rowGlobalOffset(row)" :startCustomAction="startCustomAction" :customActionIconsThreeDotsMenuItems="customActionIconsThreeDotsMenuItems ?? []" /> @@ -477,6 +479,11 @@ const showListActionsThreeDots = computed(() => { }) const from = computed(() => ((page.value || 1) - 1) * props.pageSize + 1); + +function rowGlobalOffset(row: any): number { + const indexInPage = props.rows?.indexOf(row) ?? -1; + return (page.value - 1) * props.pageSize + indexInPage; +} const to = computed(() => Math.min((page.value || 1) * props.pageSize, props.totalRows)); watch(() => page.value, (newPage) => { @@ -611,6 +618,7 @@ async function onClick(e: any, row: any) { resourceId: props.resource?.resourceId, primaryKey: row._primaryKeyValue, }, + query: { offset: rowGlobalOffset(row) }, }).href, '_blank' ); @@ -629,6 +637,7 @@ async function onClick(e: any, row: any) { resourceId: props.resource?.resourceId, primaryKey: row._primaryKeyValue, }, + query: { offset: rowGlobalOffset(row) }, }); } } diff --git a/adminforth/spa/src/views/ShowView.vue b/adminforth/spa/src/views/ShowView.vue index 75985e78d..f7719df7b 100644 --- a/adminforth/spa/src/views/ShowView.vue +++ b/adminforth/spa/src/views/ShowView.vue @@ -10,6 +10,19 @@ :adminUser="coreStore.adminUser" /> +