Skip to content

Commit 8e9def9

Browse files
ziakhan04lickem22
andauthored
Add doc validation (#532)
* add is_validated and is_active to content table * Set up backend to make validation work * add is archived * Make card count get fetched when indexing is fetched * Make contents refresh when cards are validated * Fix typeerror * Change wording of buttons * fix ui issue where card select multiple was messing up UI since there were too many buttons on screen * Address PR comment * Fix admin app bug and changed container in workflow to node:20-bookworm --------- Co-authored-by: Carlos Samey <carlossamey@gmail.com>
1 parent 381809f commit 8e9def9

File tree

12 files changed

+581
-80
lines changed

12 files changed

+581
-80
lines changed

.github/workflows/retrieval-validation.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ permissions:
1717
jobs:
1818
validate:
1919
runs-on: ubuntu-22.04
20-
container: node:20.7-bullseye
20+
container: node:20-bookworm
2121

2222
services:
2323
postgres:

.github/workflows/tests.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ env:
1515
jobs:
1616
alembic-tests:
1717
runs-on: ubuntu-22.04
18-
container: node:20.7-bullseye
18+
container: node:20-bookworm
1919
services:
2020
postgres:
2121
image: pgvector/pgvector:pg16
@@ -64,7 +64,7 @@ jobs:
6464
python -m pytest -m "not rails and alembic" tests/api/test_alembic_migrations.py
6565
unit-tests:
6666
runs-on: ubuntu-22.04
67-
container: node:20.7-bullseye
67+
container: node:20-bookworm
6868
services:
6969
postgres:
7070
image: pgvector/pgvector:pg16

admin_app/src/app/content/api.ts

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const formatDate = (dateString: string) => {
4949
second: "2-digit",
5050
}).format(date);
5151
};
52+
5253
const getContentList = async ({
5354
token,
5455
skip = 0,
@@ -81,6 +82,18 @@ const getContent = async (content_id: number, token: string) => {
8182
}
8283
};
8384

85+
const useArchiveContentCard = (token: string) => {
86+
const queryClient = useQueryClient();
87+
88+
return useMutation({
89+
mutationFn: (content_id: number) => archiveContent(content_id, token),
90+
onSuccess: () => {
91+
queryClient.invalidateQueries({ queryKey: ["unvalidatedCount"] });
92+
queryClient.invalidateQueries({ queryKey: ["nextUnvalidatedCard"] });
93+
},
94+
});
95+
};
96+
8497
const archiveContent = async (content_id: number, token: string) => {
8598
try {
8699
const response = await api.patch(
@@ -197,15 +210,17 @@ const deleteTag = async (tag_id: number, token: string) => {
197210
}
198211
};
199212

200-
const useGetIndexingStatus = () => {
201-
const { token } = useAuth();
213+
const useGetIndexingStatus = (token: string) => {
214+
const queryClient = useQueryClient();
215+
202216
return useQuery<IndexingStatusResponse>({
203217
queryKey: ["indexingStatus"],
204218
queryFn: async () => {
205219
try {
206220
const response = await api.get("/docmuncher/status/is_job_running", {
207221
headers: { Authorization: `Bearer ${token}` },
208222
});
223+
queryClient.invalidateQueries({ queryKey: ["unvalidatedCount"] });
209224
return response.data;
210225
} catch (error) {
211226
const errorMessage = "Error fetching indexing status";
@@ -219,6 +234,70 @@ const useGetIndexingStatus = () => {
219234
});
220235
};
221236

237+
const useGetUnvalidatedCardsCount = (token: string) => {
238+
return useQuery({
239+
queryKey: ["unvalidatedCount"],
240+
queryFn: async () => {
241+
try {
242+
const response = await api.get("/content/unvalidated-count", {
243+
headers: { Authorization: `Bearer ${token}` },
244+
});
245+
return response.data;
246+
} catch (error) {
247+
const errorMessage = "Error fetching unvalidated count";
248+
handleApiError(error, errorMessage);
249+
throw error;
250+
}
251+
},
252+
enabled: !!token,
253+
});
254+
};
255+
256+
const useGetNextUnvalidatedCard = (token: string, isEnabled: boolean = false) => {
257+
return useQuery({
258+
queryKey: ["nextUnvalidatedCard"],
259+
queryFn: async () => {
260+
try {
261+
const response = await api.get("/content/next-unvalidated", {
262+
headers: { Authorization: `Bearer ${token}` },
263+
});
264+
return response.data;
265+
} catch (error) {
266+
const errorMessage = "Error fetching next unvalidated card";
267+
handleApiError(error, errorMessage);
268+
throw error;
269+
}
270+
},
271+
enabled: !!token && isEnabled,
272+
});
273+
};
274+
275+
const useValidateContentCard = (token: string) => {
276+
const queryClient = useQueryClient();
277+
return useMutation({
278+
mutationFn: async (content_id: number) => {
279+
try {
280+
const response = await api.patch(
281+
`/content/validate/${content_id}`,
282+
{},
283+
{
284+
headers: { Authorization: `Bearer ${token}` },
285+
},
286+
);
287+
return response.data;
288+
} catch (error) {
289+
const errorMessage = "Error validating content card";
290+
handleApiError(error, errorMessage);
291+
throw error;
292+
}
293+
},
294+
onSuccess: () => {
295+
queryClient.invalidateQueries({ queryKey: ["unvalidatedCount"] });
296+
queryClient.invalidateQueries({ queryKey: ["nextUnvalidatedCard"] });
297+
},
298+
});
299+
};
300+
222301
const usePostDocumentToIndex = (token: string) => {
223302
const queryClient = useQueryClient();
224303
return useMutation<DocumentUploadResponse, Error, { file: File }>({
@@ -292,4 +371,8 @@ export {
292371
useGetIndexingStatus,
293372
usePostDocumentToIndex,
294373
getDocIndexingStatusData,
374+
useGetUnvalidatedCardsCount,
375+
useGetNextUnvalidatedCard,
376+
useValidateContentCard,
377+
useArchiveContentCard,
295378
};

admin_app/src/app/content/components/ContentCard.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ const ContentCard = ({
203203
negative_votes={negative_votes}
204204
onClose={() => setOpenReadModal(false)}
205205
editAccess={editAccess}
206+
setRefreshKey={function (value: React.SetStateAction<number>): void {
207+
throw new Error("Function not implemented.");
208+
}}
206209
/>
207210
<ArchiveContentModal
208211
content_id={content_id}

admin_app/src/app/content/components/ContentModal.tsx

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Link from "next/link";
1111
import { Layout } from "@/components/Layout";
1212
import { Tag } from "@/app/content/page";
1313
import { CustomError } from "@/utils/api";
14+
import { useValidateContentCard, useArchiveContentCard } from "../api";
1415

1516
const ContentViewModal = ({
1617
title,
@@ -23,20 +24,27 @@ const ContentViewModal = ({
2324
tags,
2425
open,
2526
onClose,
27+
setRefreshKey,
2628
editAccess,
29+
validation_mode = false,
2730
}: {
2831
title: string;
2932
text: string;
3033
content_id: number;
3134
display_number: number;
3235
last_modified: string;
33-
tags: Tag[];
36+
tags?: Tag[];
3437
positive_votes: number;
3538
negative_votes: number;
3639
open: boolean;
3740
onClose: () => void;
41+
setRefreshKey?: React.Dispatch<React.SetStateAction<number>>;
3842
editAccess: boolean;
43+
validation_mode?: boolean;
3944
}) => {
45+
const token = typeof window !== "undefined" ? localStorage.getItem("token") : null;
46+
const { mutate: validateCard } = useValidateContentCard(token!);
47+
const { mutate: archiveContent } = useArchiveContentCard(token!);
4048
return (
4149
<Modal
4250
open={open as boolean}
@@ -124,16 +132,51 @@ const ContentViewModal = ({
124132
paddingInline: 1,
125133
}}
126134
>
127-
<Button
128-
variant="contained"
129-
color="primary"
130-
disabled={!editAccess}
131-
component={Link}
132-
href={`/content/edit?content_id=${content_id}`}
133-
startIcon={<Edit />}
134-
>
135-
Edit
136-
</Button>
135+
{!validation_mode && (
136+
<Button
137+
variant="contained"
138+
color="primary"
139+
disabled={!editAccess}
140+
component={Link}
141+
href={`/content/edit?content_id=${content_id}`}
142+
startIcon={<Edit />}
143+
>
144+
Edit
145+
</Button>
146+
)}
147+
{validation_mode && (
148+
<Layout.FlexBox
149+
sx={{
150+
flexDirection: "row",
151+
gap: sizes.baseGap,
152+
}}
153+
>
154+
<Button
155+
variant="contained"
156+
color="primary"
157+
disabled={!editAccess}
158+
onClick={() => {
159+
validateCard(content_id);
160+
setTimeout(() => {
161+
setRefreshKey?.((prev) => prev + 1);
162+
}, 500);
163+
setRefreshKey?.((prev) => prev + 1);
164+
}}
165+
>
166+
Approve
167+
</Button>
168+
<Button
169+
variant="outlined"
170+
color="error"
171+
disabled={!editAccess}
172+
onClick={() => {
173+
archiveContent(content_id);
174+
}}
175+
>
176+
Reject
177+
</Button>
178+
</Layout.FlexBox>
179+
)}
137180
<Typography variant="body2" color={appColors.darkGrey}>
138181
Last modified{" "}
139182
{new Date(last_modified).toLocaleString(undefined, {

0 commit comments

Comments
 (0)