Skip to content

Commit 099e97b

Browse files
authored
Docmuncher UI updates (#524)
* Update indexing to PDF in UI * Fix PDF create card to generate card wording * Switch status open to zustand * Add open status button to pdf import modal * change message when PDF upload successful * Nahh change text again * Make upload-pdf trigger status-fetch using tanstack * Modify to align with PR comments
1 parent 51ce5a6 commit 099e97b

File tree

9 files changed

+222
-165
lines changed

9 files changed

+222
-165
lines changed

admin_app/package-lock.json

Lines changed: 58 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

admin_app/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@mui/icons-material": "^5.15.10",
1717
"@mui/lab": "^5.0.0-alpha.173",
1818
"@mui/material": "^5.16.5",
19+
"@tanstack/react-query": "^5.72.1",
1920
"axios": "^1.7.7",
2021
"date-fns": "^3.6.0",
2122
"jwt-decode": "^4.0.0",
@@ -27,7 +28,8 @@
2728
"react-datepicker": "^8.0.0",
2829
"react-dom": "^18",
2930
"react-dropzone": "^14.3.8",
30-
"react-markdown": "^10.1.0"
31+
"react-markdown": "^10.1.0",
32+
"zustand": "^5.0.3"
3133
},
3234
"devDependencies": {
3335
"@types/google.accounts": "^0.0.14",

admin_app/src/app/content/api.ts

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import api, { handleApiError } from "@/utils/api";
2-
2+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
3+
import { useAuth } from "@/utils/auth";
34
type IndexingStatusResponse = boolean | { detail: string };
45

56
export interface DocIndexingTask {
@@ -31,6 +32,11 @@ interface ContentBody {
3132
content_metadata: Record<string, unknown>;
3233
}
3334

35+
interface DocumentUploadResponse {
36+
status: number;
37+
detail: any;
38+
}
39+
3440
const formatDate = (dateString: string) => {
3541
if (!dateString) return "—";
3642
const date = new Date(dateString);
@@ -191,40 +197,51 @@ const deleteTag = async (tag_id: number, token: string) => {
191197
}
192198
};
193199

194-
const getIndexingStatus = async (
195-
token: string,
196-
): Promise<IndexingStatusResponse | undefined> => {
197-
try {
198-
const response = await api.get("/docmuncher/status/is_job_running", {
199-
headers: { Authorization: `Bearer ${token}` },
200-
});
201-
202-
if (response.status === 200) {
203-
return response.data;
204-
} else {
205-
throw new Error("Unexpected response status");
206-
}
207-
} catch (error) {
208-
let errorMessage = "Error fetching indexing status";
209-
handleApiError(error, errorMessage);
210-
}
200+
const useGetIndexingStatus = () => {
201+
const { token } = useAuth();
202+
return useQuery<IndexingStatusResponse>({
203+
queryKey: ["indexingStatus"],
204+
queryFn: async () => {
205+
try {
206+
const response = await api.get("/docmuncher/status/is_job_running", {
207+
headers: { Authorization: `Bearer ${token}` },
208+
});
209+
return response.data;
210+
} catch (error) {
211+
const errorMessage = "Error fetching indexing status";
212+
handleApiError(error, errorMessage);
213+
throw error;
214+
}
215+
},
216+
enabled: !!token,
217+
refetchInterval: (query) => (query.state.data === true ? 3000 : false),
218+
refetchIntervalInBackground: true,
219+
});
211220
};
212221

213-
const postDocumentToIndex = async (file: File, token: string) => {
214-
const formData = new FormData();
215-
formData.append("file", file);
222+
const usePostDocumentToIndex = (token: string) => {
223+
const queryClient = useQueryClient();
224+
return useMutation<DocumentUploadResponse, Error, { file: File }>({
225+
mutationFn: async ({ file }) => {
226+
const formData = new FormData();
227+
formData.append("file", file);
216228

217-
try {
218-
const response = await api.post("/docmuncher/upload", formData, {
219-
headers: {
220-
Authorization: `Bearer ${token}`,
221-
"Content-Type": "multipart/form-data",
222-
},
223-
});
224-
return { status: response.status, detail: response.data };
225-
} catch (error) {
226-
throw new Error("Error indexing document");
227-
}
229+
try {
230+
const response = await api.post("/docmuncher/upload", formData, {
231+
headers: {
232+
Authorization: `Bearer ${token}`,
233+
"Content-Type": "multipart/form-data",
234+
},
235+
});
236+
return { status: response.status, detail: response.data };
237+
} catch (error) {
238+
throw new Error("Error indexing document");
239+
}
240+
},
241+
onSuccess: () => {
242+
queryClient.invalidateQueries({ queryKey: ["indexingStatus"] });
243+
},
244+
});
228245
};
229246

230247
const getDocIndexingStatusData = async (
@@ -272,7 +289,7 @@ export {
272289
createTag,
273290
getTagList,
274291
deleteTag,
275-
getIndexingStatus,
276-
postDocumentToIndex,
292+
useGetIndexingStatus,
293+
usePostDocumentToIndex,
277294
getDocIndexingStatusData,
278295
};

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

Lines changed: 54 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import {
1313
import { useDropzone } from "react-dropzone";
1414
import { Layout } from "@/components/Layout";
1515
import { LoadingButton } from "@mui/lab";
16-
import { postDocumentToIndex } from "../api";
16+
import { usePostDocumentToIndex } from "../api";
1717
import { useAuth } from "@/utils/auth";
18+
import { useShowIndexingStatusStore } from "../store/indexingStatusStore";
1819

1920
interface CustomError {
2021
type: string;
@@ -31,11 +32,13 @@ export const ImportFromPDFModal: React.FC<ImportFromPDFModalProps> = ({
3132
onClose,
3233
}) => {
3334
const { token } = useAuth();
35+
const { setIsOpen: setOpenIndexHistoryModal } = useShowIndexingStatusStore();
3436
const [files, setFiles] = useState<File[]>([]);
3537
const [error, setError] = useState<string>("");
3638
const [loading, setLoading] = useState(false);
3739
const [importErrorMessages, setImportErrorMessages] = useState<string[]>([]);
3840
const [importSuccess, setImportSuccess] = useState<boolean | null>(null);
41+
const { mutate: postDocumentToIndex } = usePostDocumentToIndex(token!);
3942

4043
useEffect(() => {
4144
if (!open) {
@@ -80,59 +83,36 @@ export const ImportFromPDFModal: React.FC<ImportFromPDFModalProps> = ({
8083
setImportErrorMessages([]);
8184
// add artificial delay to show loading spinner (for UX)
8285
await new Promise((resolve) => setTimeout(resolve, 500));
83-
try {
84-
const response = await postDocumentToIndex(files[0], token!);
85-
if (response.status === 200) {
86-
setImportSuccess(true);
87-
setFiles([]);
88-
} else {
89-
console.error("Error uploading file:", response.detail);
90-
if (response.detail.errors) {
91-
const errorDescriptions = response.detail.errors.map(
92-
(error: CustomError) => error.description,
93-
);
94-
setImportErrorMessages(errorDescriptions);
95-
} else {
86+
postDocumentToIndex(
87+
{ file: files[0] },
88+
{
89+
onSuccess: () => {
90+
setImportSuccess(true);
91+
setFiles([]);
92+
},
93+
onError: (error) => {
9694
setImportErrorMessages(["An unknown error occurred"]);
97-
}
98-
}
99-
} catch (error) {
100-
console.error("Error during import:", error);
101-
setImportErrorMessages([
102-
"An unexpected error occurred. Please try again later.",
103-
]);
104-
} finally {
105-
setLoading(false);
106-
setFiles([]);
107-
}
95+
},
96+
onSettled: () => {
97+
setLoading(false);
98+
},
99+
},
100+
);
108101
} else {
109-
console.error("No file selected");
110102
setImportErrorMessages(["No file selected"]);
111103
}
112104
};
113105

114-
// modal auto-close after successful import
115-
useEffect(() => {
116-
let timerId: NodeJS.Timeout;
117-
if (importSuccess) {
118-
timerId = setTimeout(() => {
119-
onClose();
120-
window.location.reload();
121-
}, 1000);
122-
}
123-
return () => clearTimeout(timerId);
124-
}, [importSuccess]);
125-
126106
return (
127107
<Dialog open={open} onClose={onClose} maxWidth="sm" fullWidth>
128108
<DialogTitle id="import-pdf-dialog-title">
129-
Import New Content From PDF
109+
Generate New Content From PDF
130110
</DialogTitle>
131111
<DialogContent>
132112
<DialogContentText id="import-pdf-dialog-description">
133113
<Typography>
134-
You can use this feature to import new contents from PDF files into AAQ. The
135-
system will automatically create cards from file content.
114+
You can use this feature to generate new contents from PDF files into AAQ.
115+
The system will automatically create cards from file content.
136116
</Typography>
137117
<p>
138118
⚠️ Maximum file size is 100MB.
@@ -203,23 +183,45 @@ export const ImportFromPDFModal: React.FC<ImportFromPDFModalProps> = ({
203183
{importSuccess && (
204184
<>
205185
<Layout.Spacer multiplier={2} />
206-
<Alert variant="standard" severity="success">
207-
File uploaded successfully!
186+
<Alert
187+
variant="standard"
188+
severity="success"
189+
action={
190+
<Box sx={{ display: "flex", gap: 1 }}>
191+
<Button
192+
variant="outlined"
193+
color="inherit"
194+
size="small"
195+
onClick={() => {
196+
onClose();
197+
setOpenIndexHistoryModal(true);
198+
}}
199+
>
200+
Check Status
201+
</Button>
202+
</Box>
203+
}
204+
>
205+
Card generation task successfully started!
208206
</Alert>
209207
</>
210208
)}
211209
</DialogContent>
212210
<DialogActions sx={{ marginBottom: 1, marginRight: 1 }}>
213211
<Button onClick={onClose}>Cancel</Button>
214-
<LoadingButton
215-
variant="contained"
216-
disabled={files.length === 0 || loading || !!error}
217-
autoFocus
218-
loading={loading}
219-
onClick={handleSubmit}
220-
>
221-
{importSuccess ? "Imported" : "Import"}
222-
</LoadingButton>
212+
{!importSuccess && (
213+
<>
214+
<LoadingButton
215+
variant="contained"
216+
disabled={files.length === 0 || loading || !!error}
217+
autoFocus
218+
loading={loading}
219+
onClick={handleSubmit}
220+
>
221+
Import
222+
</LoadingButton>
223+
</>
224+
)}
223225
</DialogActions>
224226
</Dialog>
225227
);

0 commit comments

Comments
 (0)