Skip to content

Commit 8bd4c03

Browse files
committed
Ability to toggle back and forth from the document analytics page and preserve context
1 parent 7fdd7e1 commit 8bd4c03

File tree

4 files changed

+162
-66
lines changed

4 files changed

+162
-66
lines changed

src/ui/src/App.jsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Authenticator, ThemeProvider, useAuthenticator } from '@aws-amplify/ui-
77
import '@aws-amplify/ui-react/styles.css';
88

99
import { AppContext } from './contexts/app';
10+
import { AnalyticsProvider } from './contexts/analytics';
1011
import useAwsConfig from './hooks/use-aws-config';
1112
import useCurrentSessionCreds from './hooks/use-current-session-creds';
1213

@@ -41,9 +42,11 @@ const AppContent = () => {
4142
return (
4243
<div className="App">
4344
<AppContext.Provider value={appContextValue}>
44-
<HashRouter>
45-
<Routes />
46-
</HashRouter>
45+
<AnalyticsProvider>
46+
<HashRouter>
47+
<Routes />
48+
</HashRouter>
49+
</AnalyticsProvider>
4750
</AppContext.Provider>
4851
</div>
4952
);

src/ui/src/components/document-analytics-layout/AnalyticsQueryInput.jsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { API, Logger } from 'aws-amplify';
66
import { FormField, Textarea, Button, Grid, Box, SpaceBetween, ButtonDropdown } from '@awsui/components-react';
77
import listAnalyticsJobs from '../../graphql/queries/listAnalyticsJobs';
88
import deleteAnalyticsJob from '../../graphql/queries/deleteAnalyticsJob';
9+
import { useAnalyticsContext } from '../../contexts/analytics';
910

1011
// Custom styles for expandable textarea
1112
const textareaStyles = `
@@ -19,7 +20,9 @@ const textareaStyles = `
1920
const logger = new Logger('AnalyticsQueryInput');
2021

2122
const AnalyticsQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
22-
const [query, setQuery] = useState('');
23+
const { analyticsState, updateAnalyticsState } = useAnalyticsContext();
24+
const { currentInputText } = analyticsState;
25+
2326
const [queryHistory, setQueryHistory] = useState([]);
2427
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
2528
const [selectedOption, setSelectedOption] = useState(null);
@@ -151,15 +154,15 @@ const AnalyticsQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
151154
// Update query input when a result is selected externally
152155
useEffect(() => {
153156
if (selectedResult) {
154-
setQuery(selectedResult.query);
157+
updateAnalyticsState({ currentInputText: selectedResult.query });
155158
setSelectedOption(null); // Reset dropdown selection
156159
}
157-
}, [selectedResult]);
160+
}, [selectedResult, updateAnalyticsState]);
158161

159162
const handleSubmit = (e) => {
160163
e.preventDefault();
161-
if (query.trim() && !isSubmitting) {
162-
onSubmit(query);
164+
if (currentInputText.trim() && !isSubmitting) {
165+
onSubmit(currentInputText);
163166
setSelectedOption(null); // Reset dropdown selection after submission
164167

165168
// Refresh the query history after a short delay to include the new query
@@ -177,13 +180,12 @@ const AnalyticsQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
177180

178181
const selectedJob = queryHistory.find((job) => job.jobId === detail.id);
179182
if (selectedJob) {
180-
setQuery(selectedJob.query);
183+
updateAnalyticsState({ currentInputText: selectedJob.query });
181184
setSelectedOption({ value: selectedJob.jobId, label: selectedJob.query });
182185

183-
// If the job is completed, also submit the result to display it
184-
if (selectedJob.status === 'COMPLETED') {
185-
onSubmit(selectedJob.query, selectedJob.jobId);
186-
}
186+
// Submit the job to display its current status and results (if completed)
187+
// This will work for both completed jobs and in-progress jobs
188+
onSubmit(selectedJob.query, selectedJob.jobId);
187189
}
188190
};
189191

@@ -248,7 +250,7 @@ const AnalyticsQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
248250
// If the deleted job was currently selected, clear the selection
249251
if (selectedOption && selectedOption.value === job.jobId) {
250252
setSelectedOption(null);
251-
setQuery('');
253+
updateAnalyticsState({ currentInputText: '' });
252254
}
253255
} catch (err) {
254256
logger.error('Error deleting job:', err);
@@ -289,8 +291,8 @@ const AnalyticsQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
289291
<FormField label="Enter your analytics query">
290292
<Textarea
291293
placeholder="How has the number of documents processed per day trended over the past three weeks?"
292-
value={query}
293-
onChange={({ detail }) => setQuery(detail.value)}
294+
value={currentInputText}
295+
onChange={({ detail }) => updateAnalyticsState({ currentInputText: detail.value })}
294296
disabled={isSubmitting}
295297
rows={2}
296298
className="expandable-textarea"
@@ -299,7 +301,7 @@ const AnalyticsQueryInput = ({ onSubmit, isSubmitting, selectedResult }) => {
299301
<Box padding={{ top: 'xl' }}>
300302
{' '}
301303
{/* Add top padding to align with input box */}
302-
<Button variant="primary" type="submit" disabled={!query.trim() || isSubmitting} fullWidth>
304+
<Button variant="primary" type="submit" disabled={!currentInputText.trim() || isSubmitting} fullWidth>
303305
{isSubmitting ? 'Submitting...' : 'Submit query'}
304306
</Button>
305307
</Box>

src/ui/src/components/document-analytics-layout/DocumentsAnalyticsLayout.jsx

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: MIT-0
3-
import React, { useState, useEffect } from 'react';
3+
import React, { useEffect } from 'react';
44
import { API, Logger } from 'aws-amplify';
55
import { Container, Header, SpaceBetween, Spinner, Box } from '@awsui/components-react';
66

77
import submitAnalyticsQuery from '../../graphql/queries/submitAnalyticsQuery';
88
import getAnalyticsJobStatus from '../../graphql/queries/getAnalyticsJobStatus';
99
import onAnalyticsJobComplete from '../../graphql/subscriptions/onAnalyticsJobComplete';
10+
import { useAnalyticsContext } from '../../contexts/analytics';
1011

1112
import AnalyticsQueryInput from './AnalyticsQueryInput';
1213
import AnalyticsJobStatus from './AnalyticsJobStatus';
@@ -16,15 +17,8 @@ import AgentMessagesDisplay from './AgentMessagesDisplay';
1617
const logger = new Logger('DocumentsAnalyticsLayout');
1718

1819
const DocumentsAnalyticsLayout = () => {
19-
const [queryText, setQueryText] = useState('');
20-
const [jobId, setJobId] = useState(null);
21-
const [jobStatus, setJobStatus] = useState(null);
22-
const [jobResult, setJobResult] = useState(null);
23-
const [agentMessages, setAgentMessages] = useState(null);
24-
const [error, setError] = useState(null);
25-
const [isSubmitting, setIsSubmitting] = useState(false);
26-
const [subscription, setSubscription] = useState(null);
27-
const [selectedHistoryItem] = useState(null);
20+
const { analyticsState, updateAnalyticsState } = useAnalyticsContext();
21+
const { queryText, jobId, jobStatus, jobResult, agentMessages, error, isSubmitting, subscription } = analyticsState;
2822

2923
const subscribeToJobCompletion = (id) => {
3024
try {
@@ -53,39 +47,43 @@ const DocumentsAnalyticsLayout = () => {
5347
logger.debug('Fetched job details:', job);
5448

5549
if (job) {
56-
setJobStatus(job.status);
57-
setAgentMessages(job.agent_messages);
50+
updateAnalyticsState({
51+
jobStatus: job.status,
52+
agentMessages: job.agent_messages,
53+
});
5854

5955
if (job.status === 'COMPLETED') {
60-
setJobResult(job.result);
56+
updateAnalyticsState({ jobResult: job.result });
6157
} else if (job.status === 'FAILED') {
62-
setError(job.error || 'Job processing failed');
58+
updateAnalyticsState({ error: job.error || 'Job processing failed' });
6359
}
6460
} else {
6561
logger.error('Failed to fetch job details after completion notification');
66-
setError('Failed to fetch job details after completion');
62+
updateAnalyticsState({ error: 'Failed to fetch job details after completion' });
6763
}
6864
} catch (fetchError) {
6965
logger.error('Error fetching job details:', fetchError);
70-
setError(`Failed to fetch job details: ${fetchError.message || 'Unknown error'}`);
66+
updateAnalyticsState({ error: `Failed to fetch job details: ${fetchError.message || 'Unknown error'}` });
7167
}
7268
} else {
7369
logger.error('Received invalid completion notification. Full response:', JSON.stringify(value, null, 2));
74-
setError(`Received invalid completion notification. Check console logs for details.`);
70+
updateAnalyticsState({
71+
error: 'Received invalid completion notification. Check console logs for details.',
72+
});
7573
}
7674
},
7775
error: (err) => {
7876
logger.error('Subscription error:', err);
7977
logger.error('Error details:', JSON.stringify(err, null, 2));
80-
setError(`Subscription error: ${err.message || 'Unknown error'}`);
78+
updateAnalyticsState({ error: `Subscription error: ${err.message || 'Unknown error'}` });
8179
},
8280
});
8381

84-
setSubscription(sub);
82+
updateAnalyticsState({ subscription: sub });
8583
return sub;
8684
} catch (err) {
8785
logger.error('Error setting up subscription:', err);
88-
setError(`Failed to set up job status subscription: ${err.message || 'Unknown error'}`);
86+
updateAnalyticsState({ error: `Failed to set up job status subscription: ${err.message || 'Unknown error'}` });
8987
return null;
9088
}
9189
};
@@ -102,12 +100,15 @@ const DocumentsAnalyticsLayout = () => {
102100

103101
const handleSubmitQuery = async (query, existingJobId = null) => {
104102
try {
105-
setQueryText(query);
103+
updateAnalyticsState({
104+
queryText: query,
105+
currentInputText: query, // Also update the input text to match the submitted query
106+
});
106107

107108
// If an existing job ID is provided, fetch that job's result instead of creating a new job
108109
if (existingJobId) {
109110
logger.debug('Using existing job:', existingJobId);
110-
setJobId(existingJobId);
111+
updateAnalyticsState({ jobId: existingJobId });
111112

112113
// Fetch the job status and result
113114
const response = await API.graphql({
@@ -117,12 +118,14 @@ const DocumentsAnalyticsLayout = () => {
117118

118119
const job = response?.data?.getAnalyticsJobStatus;
119120
if (job) {
120-
setJobStatus(job.status);
121-
setAgentMessages(job.agent_messages);
121+
updateAnalyticsState({
122+
jobStatus: job.status,
123+
agentMessages: job.agent_messages,
124+
});
122125
if (job.status === 'COMPLETED') {
123-
setJobResult(job.result);
126+
updateAnalyticsState({ jobResult: job.result });
124127
} else if (job.status === 'FAILED') {
125-
setError(job.error || 'Job processing failed');
128+
updateAnalyticsState({ error: job.error || 'Job processing failed' });
126129
} else {
127130
// If job is still processing, subscribe to updates
128131
subscribeToJobCompletion(existingJobId);
@@ -132,10 +135,12 @@ const DocumentsAnalyticsLayout = () => {
132135
}
133136

134137
// Otherwise, create a new job
135-
setIsSubmitting(true);
136-
setJobResult(null);
137-
setAgentMessages(null);
138-
setError(null);
138+
updateAnalyticsState({
139+
isSubmitting: true,
140+
jobResult: null,
141+
agentMessages: null,
142+
error: null,
143+
});
139144

140145
// Clean up previous subscription if exists
141146
if (subscription) {
@@ -155,8 +160,10 @@ const DocumentsAnalyticsLayout = () => {
155160
throw new Error('Failed to create analytics job - received null response');
156161
}
157162

158-
setJobId(job.jobId);
159-
setJobStatus(job.status);
163+
updateAnalyticsState({
164+
jobId: job.jobId,
165+
jobStatus: job.status,
166+
});
160167

161168
// Subscribe to job completion
162169
subscribeToJobCompletion(job.jobId);
@@ -174,13 +181,15 @@ const DocumentsAnalyticsLayout = () => {
174181
logger.debug('Immediate poll result:', polledJob);
175182

176183
if (polledJob && polledJob.status !== job.status) {
177-
setJobStatus(polledJob.status);
178-
setAgentMessages(polledJob.agent_messages);
184+
updateAnalyticsState({
185+
jobStatus: polledJob.status,
186+
agentMessages: polledJob.agent_messages,
187+
});
179188

180189
if (polledJob.status === 'COMPLETED') {
181-
setJobResult(polledJob.result);
190+
updateAnalyticsState({ jobResult: polledJob.result });
182191
} else if (polledJob.status === 'FAILED') {
183-
setError(polledJob.error || 'Job processing failed');
192+
updateAnalyticsState({ error: polledJob.error || 'Job processing failed' });
184193
}
185194
}
186195
} catch (pollErr) {
@@ -190,10 +199,12 @@ const DocumentsAnalyticsLayout = () => {
190199
}, 1000);
191200
} catch (err) {
192201
logger.error('Error submitting query:', err);
193-
setError(err.message || 'Failed to submit query');
194-
setJobStatus('FAILED');
202+
updateAnalyticsState({
203+
error: err.message || 'Failed to submit query',
204+
jobStatus: 'FAILED',
205+
});
195206
} finally {
196-
setIsSubmitting(false);
207+
updateAnalyticsState({ isSubmitting: false });
197208
}
198209
};
199210

@@ -215,16 +226,16 @@ const DocumentsAnalyticsLayout = () => {
215226

216227
if (job) {
217228
// Always update agent messages, even if status hasn't changed
218-
setAgentMessages(job.agent_messages);
229+
updateAnalyticsState({ agentMessages: job.agent_messages });
219230

220231
if (job.status !== jobStatus) {
221-
setJobStatus(job.status);
232+
updateAnalyticsState({ jobStatus: job.status });
222233

223234
if (job.status === 'COMPLETED') {
224-
setJobResult(job.result);
235+
updateAnalyticsState({ jobResult: job.result });
225236
clearInterval(intervalId);
226237
} else if (job.status === 'FAILED') {
227-
setError(job.error || 'Job processing failed');
238+
updateAnalyticsState({ error: job.error || 'Job processing failed' });
228239
clearInterval(intervalId);
229240
}
230241
}
@@ -241,16 +252,12 @@ const DocumentsAnalyticsLayout = () => {
241252
clearInterval(intervalId);
242253
}
243254
};
244-
}, [jobId, jobStatus]);
255+
}, [jobId, jobStatus, updateAnalyticsState]);
245256

246257
return (
247258
<Container header={<Header variant="h1">Document Analytics</Header>}>
248259
<SpaceBetween size="l">
249-
<AnalyticsQueryInput
250-
onSubmit={handleSubmitQuery}
251-
isSubmitting={isSubmitting}
252-
selectedResult={selectedHistoryItem}
253-
/>
260+
<AnalyticsQueryInput onSubmit={handleSubmitQuery} isSubmitting={isSubmitting} selectedResult={null} />
254261

255262
{isSubmitting && (
256263
<Box textAlign="center" padding={{ vertical: 'l' }}>

0 commit comments

Comments
 (0)