Skip to content

Commit 04eace6

Browse files
authored
feat(GAT-8821): Correct DAR dialog & save draft logic (#1578)
1 parent 71843f9 commit 04eace6

7 files changed

Lines changed: 88 additions & 50 deletions

File tree

src/app/[locale]/account/(withoutLeftNav)/profile/data-access-requests/applications/[applicationId]/components/ApplicationSection.tsx

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ import {
6464
renderFormHydrationField,
6565
} from "@/utils/formHydration";
6666
import { updateDarApplicationAnswersAction } from "@/app/actions/updateDarApplicationAnswers";
67-
import { updateDarApplicationTeamAction } from "@/app/actions/updateDarApplicationTeam";
68-
import { updateDarApplicationUserAction } from "@/app/actions/updateDarApplicationUser";
6967
import notFound from "@/app/not-found";
7068
import { DarActionBar } from "./DarActionBar";
7169
import DarFieldArray from "./DarFieldArray";
@@ -212,7 +210,9 @@ const ApplicationSection = ({
212210
? formData[PROJECT_TITLE_FIELD]
213211
: getValues(PROJECT_TITLE_FIELD),
214212
applicant_id: data.applicant_id,
215-
submission_status: DarApplicationStatus.DRAFT,
213+
submission_status: formData
214+
? DarApplicationStatus.SUBMITTED
215+
: DarApplicationStatus.DRAFT,
216216
};
217217

218218
const values = formData ?? getValues();
@@ -224,48 +224,34 @@ const ApplicationSection = ({
224224
excludedQuestionFields
225225
);
226226

227+
const resAnswers = await updateDarApplicationAnswersAction(
228+
applicationId,
229+
userId,
230+
{
231+
...applicationData,
232+
answers,
233+
}
234+
);
235+
227236
if (formData) {
228-
const [resAnswers, resApplication] = await Promise.all([
229-
updateDarApplicationAnswersAction(applicationId, userId, {
230-
...applicationData,
231-
answers,
232-
}),
233-
isResearcher
234-
? updateDarApplicationUserAction(applicationId, userId, {
235-
submission_status: DarApplicationStatus.SUBMITTED,
236-
})
237-
: teamId &&
238-
updateDarApplicationTeamAction(applicationId, teamId, {
239-
submission_status: DarApplicationStatus.SUBMITTED,
240-
}),
241-
]);
242-
243-
if (resAnswers && resApplication) {
237+
if (resAnswers) {
238+
notificationService.apiSuccess(
239+
"Data Access Request submitted successfully"
240+
);
244241
push(
245242
`/${RouteName.ACCOUNT}/${RouteName.PROFILE}/${RouteName.DATA_ACCESS_REQUESTS}/${RouteName.APPLICATIONS}`
246243
);
247244
} else {
248245
notificationService.apiError("Failed to submit application");
249246
}
247+
} else if (resAnswers) {
248+
notificationService.apiSuccess(
249+
"Successfully updated Data Access Request"
250+
);
250251
} else {
251-
const resAnswers = await updateDarApplicationAnswersAction(
252-
applicationId,
253-
userId,
254-
{
255-
...applicationData,
256-
answers,
257-
}
252+
notificationService.apiError(
253+
"Failed to update Data Access Request"
258254
);
259-
260-
if (resAnswers) {
261-
notificationService.apiSuccess(
262-
"Successfully updated Data Access Request"
263-
);
264-
} else {
265-
notificationService.apiError(
266-
"Failed to update Data Access Request"
267-
);
268-
}
269255
}
270256
};
271257

@@ -277,6 +263,10 @@ const ApplicationSection = ({
277263
await saveApplication();
278264
};
279265

266+
const handleInvalidSubmit = () => {
267+
notificationService.apiError(t("missingRequiredFields"));
268+
};
269+
280270
const handleManageApplication = () => {
281271
showDialog(DarManageDialog, { darApplicationEndpoint, applicationId });
282272
};
@@ -287,6 +277,12 @@ const ApplicationSection = ({
287277
teamApplication &&
288278
teamApplication?.approval_status !== null);
289279

280+
const isApplicationEditable =
281+
!teamApplication ||
282+
(teamApplication?.approval_status === null &&
283+
teamApplication.submission_status !==
284+
DarApplicationStatus.SUBMITTED);
285+
290286
const renderSectionHeader = (field: DarFormattedField) => (
291287
<>
292288
<Box sx={{ pl: 3, pr: 3 }}>
@@ -566,8 +562,12 @@ const ApplicationSection = ({
566562
teamId={teamId}
567563
userId={userId}
568564
saveDraftOnClick={handleSaveAsDraft}
569-
submitOnClick={handleSubmit(handleSave)}
565+
submitOnClick={handleSubmit(
566+
handleSave,
567+
handleInvalidSubmit
568+
)}
570569
isResearcher={isResearcher}
570+
showSaveDraft={isApplicationEditable}
571571
manageApplicationOnStatus={handleManageApplication}
572572
/>
573573

@@ -799,14 +799,12 @@ const ApplicationSection = ({
799799
)}
800800

801801
<Box sx={{ gap: 1, p: 0, display: "flex" }}>
802-
{isResearcher &&
803-
(!teamApplication ||
804-
(teamApplication?.approval_status ===
805-
null &&
806-
teamApplication.submission_status !==
807-
DarApplicationStatus.SUBMITTED)) && (
802+
{isResearcher && isApplicationEditable && (
808803
<Button
809-
onClick={handleSubmit(handleSave)}
804+
onClick={handleSubmit(
805+
handleSave,
806+
handleInvalidSubmit
807+
)}
810808
type="submit"
811809
variant="outlined"
812810
color="secondary">

src/app/[locale]/account/(withoutLeftNav)/profile/data-access-requests/applications/[applicationId]/components/DarActionBar.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const defaultProps = {
88
submitOnClick: jest.fn(),
99
manageApplicationOnStatus: jest.fn(),
1010
isResearcher: true,
11+
showSaveDraft: true,
1112
};
1213

1314
const mockData = {
@@ -55,4 +56,21 @@ describe("DarActionBar", () => {
5556
expect(await screen.findByText("Dataset Alpha")).toBeInTheDocument();
5657
expect(await screen.findByText("Dataset Beta")).toBeInTheDocument();
5758
});
59+
60+
it("renders the save draft button when showSaveDraft is true", async () => {
61+
mockFetch(mockData);
62+
render(<DarActionBar {...defaultProps} />);
63+
expect(
64+
await screen.findByRole("button", { name: "Save draft" })
65+
).toBeInTheDocument();
66+
});
67+
68+
it("does not render the save draft button when showSaveDraft is false", async () => {
69+
mockFetch(mockData);
70+
render(<DarActionBar {...defaultProps} showSaveDraft={false} />);
71+
expect(await screen.findByText("My Research Project")).toBeInTheDocument();
72+
expect(
73+
screen.queryByRole("button", { name: "Save draft" })
74+
).not.toBeInTheDocument();
75+
});
5876
})

src/app/[locale]/account/(withoutLeftNav)/profile/data-access-requests/applications/[applicationId]/components/DarActionBar.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ interface DarFormHeaderProps {
3434
submitOnClick: () => Promise<void> | void | undefined;
3535
manageApplicationOnStatus: () => Promise<void> | void | undefined;
3636
isResearcher: boolean;
37+
showSaveDraft: boolean;
3738
}
3839

3940
const DarActionBar = ({
@@ -44,6 +45,7 @@ const DarActionBar = ({
4445
submitOnClick,
4546
manageApplicationOnStatus,
4647
isResearcher,
48+
showSaveDraft,
4749
}: DarFormHeaderProps) => {
4850
const idTitle = `DAR Application ${applicationId}`;
4951
const t = useTranslations(TRANSLATION_PATH);
@@ -151,9 +153,13 @@ const DarActionBar = ({
151153
sx={{ my: 2, ml: 2 }}>
152154
{isResearcher ? (
153155
<>
154-
<Button color="greyCustom" onClick={saveDraftOnClick}>
155-
{t("saveDraft")}
156-
</Button>
156+
{showSaveDraft && (
157+
<Button
158+
color="greyCustom"
159+
onClick={saveDraftOnClick}>
160+
{t("saveDraft")}
161+
</Button>
162+
)}
157163
<Button color="primary" onClick={submitOnClick}>
158164
{t("submitApplication")}
159165
</Button>

src/app/[locale]/account/profile/cohort-discovery-admin/components/CohortTable/CohortTable.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ const requests = [
99
generateCohortRequestV1({
1010
request_status: "APPROVED",
1111
nhse_sde_request_status: "IN PROCESS",
12+
created_at: "2025-01-15T00:00:00.000Z",
13+
updated_at: "2025-06-20T00:00:00.000Z",
1214
}),
1315
generateCohortRequestV1({
1416
request_status: "REJECTED",

src/components/DarStatusTracker/DarStatusTracker.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ export default function DarStatusTracker({
2222
statuses,
2323
}: DarStatusTrackerProps) {
2424
const t = useTranslations(TRANSLATION_PATH);
25+
const orderedStatuses = statuses.includes(DarApplicationStatus.DRAFT)
26+
? statuses
27+
: [DarApplicationStatus.DRAFT, ...statuses];
28+
2529
const formattedStatuses = [
26-
...statuses,
30+
...orderedStatuses,
2731
approvalStatus &&
2832
approvalStatus !== DarApplicationApprovalStatus.FEEDBACK
2933
? approvalStatus
@@ -53,6 +57,10 @@ export default function DarStatusTracker({
5357
mb: 3,
5458
}}>
5559
{formattedStatuses.map((status, index) => {
60+
if (status === DarApplicationStatus.DRAFT) {
61+
return null;
62+
}
63+
5664
const isActive = index === activeIndex;
5765
const isFuture = index > activeIndex;
5866

src/config/messages/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2112,7 +2112,9 @@
21122112
"rejectedButtonText": "Reject",
21132113
"changeStatus": "Change Application Status",
21142114
"intro": "Use the options below to log your decision about this application.",
2115-
"defaultDraftMessage": "This is an automated notification that the application has been changed from 'In review' to 'Draft' status. No comment has been added by the Data Custodian."
2115+
"defaultDraftMessage": "This is an automated notification that the application has been changed from 'In review' to 'Draft' status. No comment has been added by the Data Custodian.",
2116+
"statusUpdateSuccess": "Application status updated successfully",
2117+
"statusUpdateError": "Failed to update application status"
21162118
},
21172119
"DarActionDialog": {
21182120
"actionPermanent": "I understand this action is permanent",

src/modules/DarManageDialog/DarManageDialog.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Form from "@/components/Form";
1313
import InputWrapper from "@/components/InputWrapper";
1414
import Typography from "@/components/Typography";
1515
import useModal from "@/hooks/useModal";
16+
import notificationService from "@/services/notification";
1617
import { inputComponents } from "@/config/forms";
1718
import { colors } from "@/config/theme";
1819
import { CACHE_DAR_REVIEWS } from "@/consts/cache";
@@ -86,12 +87,15 @@ const DarManageDialog = ({ applicationId }: DarManageDialogProps) => {
8687
payload
8788
);
8889

89-
revalidateCacheAction(`${CACHE_DAR_REVIEWS}${applicationId}`);
90+
await revalidateCacheAction(`${CACHE_DAR_REVIEWS}${applicationId}`);
9091

9192
hideModal();
9293

9394
if (updateResponse) {
95+
notificationService.apiSuccess(t("statusUpdateSuccess"));
9496
push(redirectUrl);
97+
} else {
98+
notificationService.apiError(t("statusUpdateError"));
9599
}
96100
};
97101

0 commit comments

Comments
 (0)