Skip to content

Commit a218539

Browse files
[dev] [Marfuen] mariano/security-questionnaire-ui (#1764)
* refactor(security-questionnaire): reorganize imports and update header text * chore(security-questionnaire): enhance auto-answer button and add error handling for unanswered questions * fix(security-questionnaire): prevent token mismatch by clearing parse state before new analysis --------- Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
1 parent b00fa68 commit a218539

File tree

3 files changed

+57
-38
lines changed

3 files changed

+57
-38
lines changed

apps/app/src/app/(app)/[orgId]/security-questionnaire/hooks/useQuestionnaireActions.ts

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,27 @@ interface UseQuestionnaireActionsProps {
2727
setQuestionStatuses: React.Dispatch<
2828
React.SetStateAction<Map<number, 'pending' | 'processing' | 'completed'>>
2929
>;
30-
uploadFileAction: {
31-
execute: (payload: any) => void;
32-
status: 'idle' | 'executing' | 'hasSucceeded' | 'hasErrored' | 'transitioning' | 'hasNavigated';
33-
};
34-
parseAction: {
35-
execute: (payload: any) => void;
36-
status: 'idle' | 'executing' | 'hasSucceeded' | 'hasErrored' | 'transitioning' | 'hasNavigated';
37-
};
38-
triggerAutoAnswer: (payload: {
39-
vendorId: string;
40-
organizationId: string;
41-
questionsAndAnswers: QuestionAnswer[];
42-
}) => void;
43-
triggerSingleAnswer: (payload: {
44-
question: string;
45-
organizationId: string;
46-
questionIndex: number;
47-
totalQuestions: number;
48-
}) => void;
30+
setParseTaskId: (id: string | null) => void;
31+
setParseToken: (token: string | null) => void;
32+
uploadFileAction: {
33+
execute: (payload: any) => void;
34+
status: 'idle' | 'executing' | 'hasSucceeded' | 'hasErrored' | 'transitioning' | 'hasNavigated';
35+
};
36+
parseAction: {
37+
execute: (payload: any) => void;
38+
status: 'idle' | 'executing' | 'hasSucceeded' | 'hasErrored' | 'transitioning' | 'hasNavigated';
39+
};
40+
triggerAutoAnswer: (payload: {
41+
vendorId: string;
42+
organizationId: string;
43+
questionsAndAnswers: QuestionAnswer[];
44+
}) => void;
45+
triggerSingleAnswer: (payload: {
46+
question: string;
47+
organizationId: string;
48+
questionIndex: number;
49+
totalQuestions: number;
50+
}) => void;
4951
}
5052

5153
export function useQuestionnaireActions({
@@ -66,11 +68,13 @@ export function useQuestionnaireActions({
6668
answeringQuestionIndex,
6769
setAnsweringQuestionIndex,
6870
setQuestionStatuses,
69-
uploadFileAction,
70-
parseAction,
71-
triggerAutoAnswer,
72-
triggerSingleAnswer,
73-
}: UseQuestionnaireActionsProps) {
71+
setParseTaskId,
72+
setParseToken,
73+
uploadFileAction,
74+
parseAction,
75+
triggerAutoAnswer,
76+
triggerSingleAnswer,
77+
}: UseQuestionnaireActionsProps) {
7478
const exportAction = useAction(exportQuestionnaire, {
7579
onSuccess: ({ data }: { data: any }) => {
7680
const responseData = data?.data || data;
@@ -106,6 +110,9 @@ export function useQuestionnaireActions({
106110
}, [setSelectedFile]);
107111

108112
const handleParse = async () => {
113+
// Clear old parse state before starting new parse to prevent token mismatch
114+
setParseTaskId(null);
115+
setParseToken(null);
109116
setIsParseProcessStarted(true);
110117

111118
if (selectedFile) {

apps/app/src/app/(app)/[orgId]/security-questionnaire/hooks/useQuestionnaireParse.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

3-
import { useRealtimeRun } from '@trigger.dev/react-hooks';
43
import type { parseQuestionnaireTask } from '@/jobs/tasks/vendors/parse-questionnaire';
4+
import { useRealtimeRun } from '@trigger.dev/react-hooks';
55
import { useAction } from 'next-safe-action/hooks';
66
import { useEffect } from 'react';
77
import { toast } from 'sonner';
@@ -96,7 +96,9 @@ export function useQuestionnaireParse({
9696
setExtractedContent(extractedContent || null);
9797
setQuestionStatuses(new Map());
9898
setHasClickedAutoAnswer(false);
99-
toast.success(`Successfully parsed ${questionsAndAnswers.length} question-answer pairs`);
99+
toast.success(
100+
`Successfully parsed ${questionsAndAnswers.length} question-answer pairs`,
101+
);
100102
} else {
101103
toast.error('Parsed data is missing questions');
102104
}
@@ -137,6 +139,8 @@ export function useQuestionnaireParse({
137139
return;
138140
}
139141

142+
// Clear old token before setting new task ID to prevent using wrong token with new run
143+
setParseToken(null);
140144
setParseTaskId(taskId);
141145

142146
const tokenResult = await createRunReadToken(taskId);
@@ -185,4 +189,3 @@ export function useQuestionnaireParse({
185189
uploadFileAction,
186190
};
187191
}
188-

apps/app/src/app/(app)/[orgId]/security-questionnaire/hooks/useQuestionnaireParser.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useMemo, useState, useEffect, useRef } from 'react';
3+
import { useEffect, useMemo, useRef, useState } from 'react';
44
import { useQuestionnaireActions } from './useQuestionnaireActions';
55
import { useQuestionnaireAutoAnswer } from './useQuestionnaireAutoAnswer';
66
import { useQuestionnaireParse } from './useQuestionnaireParse';
@@ -65,11 +65,13 @@ export function useQuestionnaireParser() {
6565
answeringQuestionIndex: state.answeringQuestionIndex,
6666
setAnsweringQuestionIndex: state.setAnsweringQuestionIndex,
6767
setQuestionStatuses: state.setQuestionStatuses,
68-
uploadFileAction: parse.uploadFileAction,
69-
parseAction: parse.parseAction,
70-
triggerAutoAnswer: autoAnswer.triggerAutoAnswer,
71-
triggerSingleAnswer: singleAnswer.triggerSingleAnswer,
72-
});
68+
setParseTaskId: state.setParseTaskId,
69+
setParseToken: state.setParseToken,
70+
uploadFileAction: parse.uploadFileAction,
71+
parseAction: parse.parseAction,
72+
triggerAutoAnswer: autoAnswer.triggerAutoAnswer,
73+
triggerSingleAnswer: singleAnswer.triggerSingleAnswer,
74+
});
7375

7476
const isLoading = useMemo(() => {
7577
const isUploading = parse.uploadFileAction.status === 'executing';
@@ -167,7 +169,9 @@ export function useQuestionnaireParser() {
167169
]);
168170

169171
// Throttled status for smooth transitions
170-
const [parseStatus, setParseStatus] = useState<'uploading' | 'starting' | 'queued' | 'analyzing' | 'processing' | null>(null);
172+
const [parseStatus, setParseStatus] = useState<
173+
'uploading' | 'starting' | 'queued' | 'analyzing' | 'processing' | null
174+
>(null);
171175
const statusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
172176
const lastStatusRef = useRef<string | null>(null);
173177
const statusStartTimeRef = useRef<number | null>(null);
@@ -207,12 +211,17 @@ export function useQuestionnaireParser() {
207211
statusStartTimeRef.current = Date.now();
208212
} else {
209213
// Check if current status has been visible for minimum duration
210-
const isEarlyStage = lastStatusRef.current === 'uploading' || lastStatusRef.current === 'starting' || lastStatusRef.current === 'queued';
214+
const isEarlyStage =
215+
lastStatusRef.current === 'uploading' ||
216+
lastStatusRef.current === 'starting' ||
217+
lastStatusRef.current === 'queued';
211218
const minDisplayTime = isEarlyStage ? 3000 : 1500; // 3s minimum for early stages, 1.5s for later
212-
213-
const timeSinceStatusStart = statusStartTimeRef.current ? Date.now() - statusStartTimeRef.current : 0;
219+
220+
const timeSinceStatusStart = statusStartTimeRef.current
221+
? Date.now() - statusStartTimeRef.current
222+
: 0;
214223
const remainingTime = Math.max(0, minDisplayTime - timeSinceStatusStart);
215-
224+
216225
statusTimeoutRef.current = setTimeout(() => {
217226
setParseStatus(rawParseStatus);
218227
lastStatusRef.current = rawParseStatus;

0 commit comments

Comments
 (0)