Skip to content

Commit 55b3661

Browse files
committed
added new folder meta panel to bulk update sdoc metadata
1 parent 414aaa2 commit 55b3661

File tree

18 files changed

+649
-265
lines changed

18 files changed

+649
-265
lines changed

frontend/src/api/FolderHooks.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ const useGetAllSdocFolders = () =>
6262

6363
const useGetAllSdocFoldersMap = () => useProjectFoldersQuery({ folderType: FolderType.SDOC_FOLDER });
6464

65+
const useGetSdocIdsPerDoctypeInSdocFolder = (sdocFolderId: number | null | undefined) =>
66+
useQuery({
67+
queryKey: [QueryKey.SDOC_IDS_PER_DOCTYPE_IN_FOLDER, sdocFolderId],
68+
queryFn: () =>
69+
FolderService.getSdocIdsInFolderByDoctype({
70+
folderId: sdocFolderId!,
71+
}),
72+
staleTime: 1000 * 60 * 5,
73+
enabled: !!sdocFolderId,
74+
});
75+
6576
// Folder MUTATIONS
6677

6778
const useCreateFolder = () =>
@@ -142,6 +153,7 @@ const FolderHooks = {
142153
useGetSdocFolder,
143154
useGetAllSdocFolders,
144155
useGetAllSdocFoldersMap,
156+
useGetSdocIdsPerDoctypeInSdocFolder,
145157
useCreateFolder,
146158
useUpdateFolder,
147159
useMoveFolders,

frontend/src/api/MetadataHooks.ts

Lines changed: 82 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMutation, useQuery } from "@tanstack/react-query";
1+
import { useMutation, useQueries, useQuery, UseQueryResult } from "@tanstack/react-query";
22
import queryClient from "../plugins/ReactQueryClient.ts";
33
import { useAppSelector } from "../plugins/ReduxHooks.ts";
44
import { RootState } from "../store/store.ts";
@@ -86,11 +86,7 @@ const useDeleteProjectMetadata = () =>
8686
.forEach((query) => {
8787
queryClient.setQueryData<SdocMetadataMap>(query.queryKey, (oldData) => {
8888
const newData = { ...oldData };
89-
const metadataToDelete = Object.values(newData).find(
90-
(metadata) => metadata.project_metadata_id === data.id,
91-
);
92-
if (!metadataToDelete) return newData;
93-
delete newData[metadataToDelete.id];
89+
delete newData[data.id];
9490
return newData;
9591
});
9692
});
@@ -109,26 +105,28 @@ const useDeleteProjectMetadata = () =>
109105
});
110106

111107
// SDOC METADATA QUERIES
112-
108+
// mapping from projectMetadataId -> SourceDocumentRead
113109
export type SdocMetadataMap = Record<number, SourceDocumentMetadataRead>;
114110

115111
interface UseSdocMetadataQueryParams<T> {
116112
sdocId: number | undefined | null;
117113
select?: (data: SdocMetadataMap) => T;
118114
}
119115

116+
const sdocMetadataQueryFn = async (sdocId: number) => {
117+
const metadata = await SdocMetadataService.getBySdoc({
118+
sdocId: sdocId!,
119+
});
120+
return metadata.reduce((acc, metadata) => {
121+
acc[metadata.project_metadata_id] = metadata;
122+
return acc;
123+
}, {} as SdocMetadataMap);
124+
};
125+
120126
const useSdocMetadataQuery = <T = SdocMetadataMap>({ sdocId, select }: UseSdocMetadataQueryParams<T>) =>
121127
useQuery({
122128
queryKey: [QueryKey.SDOC_METADATAS, sdocId],
123-
queryFn: async () => {
124-
const metadata = await SdocMetadataService.getBySdoc({
125-
sdocId: sdocId!,
126-
});
127-
return metadata.reduce((acc, metadata) => {
128-
acc[metadata.id] = metadata;
129-
return acc;
130-
}, {} as SdocMetadataMap);
131-
},
129+
queryFn: () => sdocMetadataQueryFn(sdocId!),
132130
staleTime: 1000 * 60 * 5,
133131
select,
134132
enabled: !!sdocId,
@@ -143,7 +141,72 @@ const useGetSdocMetadata = (sdocId: number | null | undefined) =>
143141
const useGetSdocMetadataByProjectMetadataId = (sdocId: number | null | undefined, projectMetadataId: number) =>
144142
useSdocMetadataQuery({
145143
sdocId,
146-
select: (data) => Object.values(data).find((metadata) => metadata.project_metadata_id === projectMetadataId),
144+
select: (data) => data[projectMetadataId],
145+
});
146+
147+
export type SourceDocumentMetadataReadCombined = Omit<SourceDocumentMetadataRead, "id" | "source_document_id"> & {
148+
ids: number[];
149+
};
150+
151+
// referential stability for bulk queries
152+
const combineBulkData = (results: UseQueryResult<SdocMetadataMap, Error>[]) => {
153+
const isSuccess = results.every((result) => result.isSuccess);
154+
155+
let data: SourceDocumentMetadataReadCombined[] | undefined = undefined;
156+
if (isSuccess) {
157+
// Aggregate all SourceDocumentMetadataRead objects by project_metadata_id
158+
const combinedMap: Record<number, SourceDocumentMetadataRead[]> = {};
159+
results.forEach((result) => {
160+
Object.entries(result.data).forEach(([projectMetadataId, metadata]) => {
161+
const pmid = Number(projectMetadataId);
162+
if (!combinedMap[pmid]) {
163+
combinedMap[pmid] = [metadata];
164+
} else {
165+
combinedMap[pmid].push(metadata);
166+
}
167+
});
168+
});
169+
170+
// Merge values for each project_metadata_id
171+
data = Object.entries(combinedMap).map(([projectMetadataId, metadatas]) => {
172+
// Helper to merge a field
173+
function mergeField<T>(getter: (m: SourceDocumentMetadataRead) => T): T | null {
174+
const values = metadatas.map(getter);
175+
const first = values[0];
176+
return values.every((v) => v === first) ? first : null;
177+
}
178+
179+
return {
180+
int_value: mergeField((m) => m.int_value),
181+
str_value: mergeField((m) => m.str_value),
182+
boolean_value: mergeField((m) => m.boolean_value),
183+
date_value: mergeField((m) => m.date_value),
184+
list_value:
185+
mergeField((m) => JSON.stringify(m.list_value)) === JSON.stringify(metadatas[0].list_value)
186+
? metadatas[0].list_value
187+
: null,
188+
ids: metadatas.map((m) => m.id),
189+
project_metadata_id: Number(projectMetadataId),
190+
};
191+
});
192+
}
193+
194+
return {
195+
isLoading: results.some((result) => result.isLoading),
196+
isError: results.some((result) => result.isError),
197+
isSuccess,
198+
data,
199+
};
200+
};
201+
202+
const useGetSdocMetadataBulk = (sdocIds: number[]) =>
203+
useQueries({
204+
queries: sdocIds.map((sdocId) => ({
205+
queryKey: [QueryKey.SDOC_METADATAS, sdocId],
206+
queryFn: () => sdocMetadataQueryFn(sdocId),
207+
staleTime: 1000 * 60 * 5,
208+
})),
209+
combine: combineBulkData,
147210
});
148211

149212
// TODO: REMOVE THIS HOOK
@@ -160,26 +223,13 @@ const useGetSdocMetadataByKey = (sdocId: number | null | undefined, key: string)
160223

161224
// SDOC METADATA MUTATIONS
162225

163-
const useUpdateSdocMetadata = () =>
164-
useMutation({
165-
mutationFn: SdocMetadataService.updateById,
166-
onSuccess: (metadata) => {
167-
queryClient.setQueryData<SdocMetadataMap>([QueryKey.SDOC_METADATAS, metadata.source_document_id], (old) =>
168-
old ? { ...old, [metadata.id]: metadata } : { [metadata.id]: metadata },
169-
);
170-
},
171-
meta: {
172-
successMessage: (data: SourceDocumentMetadataRead) => `Updated metadata value for document "${data.id}"`,
173-
},
174-
});
175-
176226
const useUpdateBulkSdocMetadata = () =>
177227
useMutation({
178228
mutationFn: SdocMetadataService.updateBulk,
179229
onSuccess: (metadatas) => {
180230
metadatas.forEach((metadata) => {
181231
queryClient.setQueryData<SdocMetadataMap>([QueryKey.SDOC_METADATAS, metadata.source_document_id], (old) =>
182-
old ? { ...old, [metadata.id]: metadata } : { [metadata.id]: metadata },
232+
old ? { ...old, [metadata.project_metadata_id]: metadata } : { [metadata.project_metadata_id]: metadata },
183233
);
184234
});
185235
},
@@ -193,7 +243,7 @@ const MetadataHooks = {
193243
useGetSdocMetadata,
194244
useGetSdocMetadataByKey,
195245
useGetSdocMetadataByProjectMetadataId,
196-
useUpdateSdocMetadata,
246+
useGetSdocMetadataBulk,
197247
useUpdateBulkSdocMetadata,
198248
// project metadata
199249
useGetProjectMetadataList,

frontend/src/api/QueryKey.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ export const QueryKey = {
146146
// managed by FolderHooks
147147
// all FolderRead[] of a Project (by project id and folder type)
148148
PROJECT_FOLDERS: "projectFolders",
149+
// a dict of {doctype: list[sdocIds]} (by sdoc folder id)
150+
SDOC_IDS_PER_DOCTYPE_IN_FOLDER: "sdocIdsPerDoctypeInFolder",
149151

150152
// managed by DocProcessingHooks
151153
// all SourceDocumentStatusSimple[] of a project (by project id, status)

frontend/src/api/openapi/services/FolderService.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,27 @@ export class FolderService {
6969
},
7070
});
7171
}
72+
/**
73+
* Returns lists of source document ids per doctype in the specified sdoc folder
74+
* @returns number Successful Response
75+
* @throws ApiError
76+
*/
77+
public static getSdocIdsInFolderByDoctype({
78+
folderId,
79+
}: {
80+
folderId: number;
81+
}): CancelablePromise<Record<string, Array<number>>> {
82+
return __request(OpenAPI, {
83+
method: "GET",
84+
url: "/folder/sdocids/{folder_id}",
85+
path: {
86+
folder_id: folderId,
87+
},
88+
errors: {
89+
422: `Validation Error`,
90+
},
91+
});
92+
}
7293
/**
7394
* Returns the folders of the folder_type of the project with the given ID
7495
* @returns FolderRead Successful Response

0 commit comments

Comments
 (0)